-
+
+
+ {{ content }}
+
+
+ {{ content }}
+
+
+
{{ content }}
-
+
+
+ {{ content }}
+
+
+
{{ content }}
diff --git a/app/assets/javascripts/pages/groups/new/index.js b/app/assets/javascripts/pages/groups/new/index.js
index 7c409010510..7dab5258b24 100644
--- a/app/assets/javascripts/pages/groups/new/index.js
+++ b/app/assets/javascripts/pages/groups/new/index.js
@@ -16,9 +16,18 @@ BindInOut.initAll();
initFilePickers();
function initNewGroupCreation(el) {
- const { hasErrors, verificationRequired, verificationFormUrl, subscriptionsUrl } = el.dataset;
+ const {
+ hasErrors,
+ parentGroupName,
+ importExistingGroupPath,
+ verificationRequired,
+ verificationFormUrl,
+ subscriptionsUrl,
+ } = el.dataset;
const props = {
+ parentGroupName,
+ importExistingGroupPath,
hasErrors: parseBoolean(hasErrors),
};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js b/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js
index b551cd2fd60..bc84459e298 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js
@@ -34,6 +34,36 @@ const nonStandardEvents = {
},
counter: {},
},
+ metrics: {
+ uniqueUser: {
+ expand: ['i_testing_metrics_report_widget_total'],
+ },
+ counter: {},
+ },
+ browserPerformance: {
+ uniqueUser: {
+ expand: ['i_testing_web_performance_widget_total'],
+ },
+ counter: {},
+ },
+ licenseCompliance: {
+ uniqueUser: {
+ expand: ['i_testing_license_compliance_widget_total'],
+ },
+ counter: {},
+ },
+ loadPerformance: {
+ uniqueUser: {
+ expand: ['i_testing_load_performance_widget_total'],
+ },
+ counter: {},
+ },
+ statusChecks: {
+ uniqueUser: {
+ expand: ['i_testing_status_checks_widget'],
+ },
+ counter: {},
+ },
};
function combineDeepArray(path, ...objects) {
diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/accessibility/index.js b/app/assets/javascripts/vue_merge_request_widget/extensions/accessibility/index.js
index 22e907f7e48..c3fe70cb5df 100644
--- a/app/assets/javascripts/vue_merge_request_widget/extensions/accessibility/index.js
+++ b/app/assets/javascripts/vue_merge_request_widget/extensions/accessibility/index.js
@@ -6,7 +6,6 @@ import { EXTENSION_ICONS } from '../../constants';
export default {
name: 'WidgetAccessibility',
enablePolling: true,
- telemetry: false,
i18n: {
loading: s__('Reports|Accessibility scanning results are being parsed'),
error: s__('Reports|Accessibility scanning failed loading results'),
diff --git a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
index a0d8ca117a4..2b9804796ae 100644
--- a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
+++ b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
@@ -14,6 +14,7 @@ import { glEmojiTag } from '~/emoji';
import createFlash from '~/flash';
import { followUser, unfollowUser } from '~/rest_api';
import { isUserBusy } from '~/set_status_modal/utils';
+import Tracking from '~/tracking';
import { USER_POPOVER_DELAY } from './constants';
const MAX_SKELETON_LINES = 4;
@@ -37,6 +38,7 @@ export default {
directives: {
SafeHtml: GlSafeHtmlDirective,
},
+ mixins: [Tracking.mixin()],
props: {
target: {
type: HTMLElement,
@@ -117,6 +119,11 @@ export default {
},
async follow() {
this.toggleFollowLoading = true;
+
+ this.track('click_button', {
+ label: 'follow_from_user_popover',
+ });
+
try {
await followUser(this.user.id);
this.$emit('follow');
@@ -132,6 +139,11 @@ export default {
},
async unfollow() {
this.toggleFollowLoading = true;
+
+ this.track('click_button', {
+ label: 'unfollow_from_user_popover',
+ });
+
try {
await unfollowUser(this.user.id);
this.$emit('unfollow');
diff --git a/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue b/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue
index 8e9b8ef3e6f..232749a2d01 100644
--- a/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue
+++ b/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue
@@ -125,7 +125,7 @@ export default {
{{ activePanel.title }}
{{ details }}
-
+
diff --git a/app/components/pajamas/button_component.html.haml b/app/components/pajamas/button_component.html.haml
index 8ce7d9e0315..5cf57deb7f1 100644
--- a/app/components/pajamas/button_component.html.haml
+++ b/app/components/pajamas/button_component.html.haml
@@ -1,4 +1,4 @@
-= content_tag tag, {**@button_options, **base_attributes, class: button_class, href: @href, target: @target } do
+= content_for :pajamas_button_content, flush: true do
- if @loading
= gl_loading_icon(inline: true, css_class: 'gl-button-icon gl-button-loading-indicator')
- if @icon && (!@loading || content)
@@ -6,3 +6,10 @@
- if content
%span.gl-button-text{ class: @button_text_classes }
= content
+
+- if link?
+ = link_to @href, { **@button_options, **base_attributes, class: button_class, target: @target, method: @method } do
+ = content_for :pajamas_button_content
+- else
+ = content_tag 'button', { **@button_options, **base_attributes, class: button_class } do
+ = content_for :pajamas_button_content
diff --git a/app/components/pajamas/button_component.rb b/app/components/pajamas/button_component.rb
index c6193d1ae05..4233e446d5b 100644
--- a/app/components/pajamas/button_component.rb
+++ b/app/components/pajamas/button_component.rb
@@ -13,6 +13,7 @@ module Pajamas
# @param [String] icon
# @param [String] href
# @param [String] target
+ # @param [Symbol] method
# @param [Hash] button_options
# @param [String] button_text_classes
# @param [String] icon_classes
@@ -28,6 +29,7 @@ module Pajamas
icon: nil,
href: nil,
target: nil,
+ method: nil,
button_options: {},
button_text_classes: nil,
icon_classes: nil
@@ -43,6 +45,7 @@ module Pajamas
@icon = icon
@href = href
@target = filter_attribute(target, TARGET_OPTIONS)
+ @method = filter_attribute(method, METHOD_OPTIONS)
@button_options = button_options
@button_text_classes = button_text_classes
@icon_classes = icon_classes
@@ -75,6 +78,7 @@ module Pajamas
SIZE_OPTIONS = [:small, :medium].freeze
TYPE_OPTIONS = [:button, :reset, :submit].freeze
TARGET_OPTIONS = %w[_self _blank _parent _top].freeze
+ METHOD_OPTIONS = [:get, :post, :put, :delete, :patch].freeze
CATEGORY_CLASSES = {
primary: '',
@@ -101,8 +105,8 @@ module Pajamas
delegate :sprite_icon, to: :helpers
delegate :gl_loading_icon, to: :helpers
- def tag
- @href ? 'a' : 'button'
+ def link?
+ @href.present?
end
def base_attributes
diff --git a/app/graphql/mutations/ci/pipeline/cancel.rb b/app/graphql/mutations/ci/pipeline/cancel.rb
index 3ec6eee9f54..c52e3b4f4b8 100644
--- a/app/graphql/mutations/ci/pipeline/cancel.rb
+++ b/app/graphql/mutations/ci/pipeline/cancel.rb
@@ -13,7 +13,6 @@ module Mutations
if pipeline.cancelable?
pipeline.cancel_running
- pipeline.cancel
{ success: true, errors: [] }
else
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 4751e64ba0e..bb92792de2d 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -134,6 +134,13 @@ module GroupsHelper
@group_projects_sort || @sort || params[:sort] || sort_value_recently_created
end
+ def subgroup_creation_data(group)
+ {
+ parent_group_name: group.parent&.name,
+ import_existing_group_path: new_group_path(parent_id: group.parent_id, anchor: 'import-group-pane')
+ }
+ end
+
def verification_for_group_creation_data
# overridden in EE
{}
@@ -144,11 +151,9 @@ module GroupsHelper
false
end
- def group_name_and_path_app_data(group)
- parent = group.parent
-
+ def group_name_and_path_app_data
{
- base_path: URI.join(root_url, parent&.full_path || "").to_s,
+ base_path: root_url,
mattermost_enabled: Gitlab.config.mattermost.enabled.to_s
}
end
@@ -156,7 +161,7 @@ module GroupsHelper
def subgroups_and_projects_list_app_data(group)
{
show_schema_markup: 'true',
- new_subgroup_path: new_group_path(parent_id: group.id),
+ new_subgroup_path: new_group_path(parent_id: group.id, anchor: 'create-group-pane'),
new_project_path: new_project_path(namespace_id: group.id),
new_subgroup_illustration: image_path('illustrations/subgroup-create-new-sm.svg'),
new_project_illustration: image_path('illustrations/project-create-new-sm.svg'),
diff --git a/app/helpers/nav/new_dropdown_helper.rb b/app/helpers/nav/new_dropdown_helper.rb
index fb8fafe59f3..dc7d8049556 100644
--- a/app/helpers/nav/new_dropdown_helper.rb
+++ b/app/helpers/nav/new_dropdown_helper.rb
@@ -42,7 +42,7 @@ module Nav
::Gitlab::Nav::TopNavMenuItem.build(
id: 'new_subgroup',
title: _('New subgroup'),
- href: new_group_path(parent_id: group.id),
+ href: new_group_path(parent_id: group.id, anchor: 'create-group-pane'),
data: { track_action: 'click_link_new_subgroup', track_label: 'plus_menu_dropdown' }
)
)
diff --git a/app/mailers/emails/admin_notification.rb b/app/mailers/emails/admin_notification.rb
index 9d02d4132a1..3766b4447d1 100644
--- a/app/mailers/emails/admin_notification.rb
+++ b/app/mailers/emails/admin_notification.rb
@@ -15,23 +15,7 @@ module Emails
email = user.notification_email_or_default
mail to: email, subject: "Unsubscribed from GitLab administrator notifications"
end
-
- def user_auto_banned_email(admin_id, user_id, max_project_downloads:, within_seconds:, group: nil)
- admin = User.find(admin_id)
- @user = User.find(user_id)
- @max_project_downloads = max_project_downloads
- @within_minutes = within_seconds / 60
- @ban_scope = if group.present?
- _('your group (%{group_name})' % { group_name: group.name })
- else
- _('your GitLab instance')
- end
-
- Gitlab::I18n.with_locale(admin.preferred_language) do
- email_with_layout(
- to: admin.notification_email_or_default,
- subject: subject(_("We've detected unusual activity")))
- end
- end
end
end
+
+Emails::AdminNotification.prepend_mod
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index 7bcc97914e5..a254690de72 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -9,7 +9,7 @@
= _('Update your group name, description, avatar, and visibility.')
= link_to _('Learn more about groups.'), help_page_path('user/group/index')
.col-lg-8
- = render 'shared/group_form', f: f
+ = render 'shared/groups/group_name_and_path_fields', f: f
= render 'shared/group_form_description', f: f
.form-group.gl-form-group{ role: 'group' }
= f.label :avatar, _("Group avatar"), class: 'gl-display-block col-form-label'
diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml
index bd893ca3162..2911e9991f2 100644
--- a/app/views/groups/_home_panel.html.haml
+++ b/app/views/groups/_home_panel.html.haml
@@ -33,7 +33,10 @@
.js-vue-notification-dropdown{ data: { disabled: emails_disabled.to_s, dropdown_items: notification_dropdown_items(@notification_setting).to_json, notification_level: @notification_setting.level, help_page_path: help_page_path('user/profile/notifications'), group_id: @group.id, container_class: 'gl-mx-2 gl-mt-3 gl-vertical-align-top', no_flip: 'true' } }
- if can_create_subgroups
.gl-px-2.gl-sm-w-auto.gl-w-full
- = link_to _("New subgroup"), new_group_path(parent_id: @group.id), class: "btn btn-default gl-button gl-mt-3 gl-sm-w-auto gl-w-full", data: { qa_selector: 'new_subgroup_button' }
+ = link_to _("New subgroup"),
+ new_group_path(parent_id: @group.id, anchor: 'create-group-pane'),
+ class: "btn btn-default gl-button gl-mt-3 gl-sm-w-auto gl-w-full",
+ data: { qa_selector: 'new_subgroup_button' }
- if can_create_projects
.gl-px-2.gl-sm-w-auto.gl-w-full
= link_to _("New project"), new_project_path(namespace_id: @group.id), class: "btn btn-confirm gl-button gl-mt-3 gl-sm-w-auto gl-w-full", data: { qa_selector: 'new_project_button' }
diff --git a/app/views/groups/_new_group_fields.html.haml b/app/views/groups/_new_group_fields.html.haml
index 0527d38159b..632884051f0 100644
--- a/app/views/groups/_new_group_fields.html.haml
+++ b/app/views/groups/_new_group_fields.html.haml
@@ -1,31 +1,34 @@
+- parent = @group.parent
+- submit_label = parent ? s_('GroupsNew|Create subgroup') : s_('GroupsNew|Create group')
= form_errors(@group, pajamas_alert: true)
-= render 'shared/group_form', f: f, autofocus: true
+= render 'shared/groups/group_name_and_path_fields', f: f, autofocus: true, new_subgroup: !!parent
-.row
- .form-group.gl-form-group.col-sm-12
- %label.label-bold
- = _('Visibility level')
- %p
- = _('Who will be able to see this group?')
- = link_to _('View the documentation'), help_page_path("user/public_access"), target: '_blank', rel: 'noopener noreferrer'
- = render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group, with_label: false
-
-- if Gitlab.config.mattermost.enabled
+- unless parent
.row
- = render 'create_chat_team', f: f
+ .form-group.gl-form-group.col-sm-12
+ %label.label-bold
+ = _('Visibility level')
+ %p
+ = _('Who will be able to see this group?')
+ = link_to _('View the documentation'), help_page_path("user/public_access"), target: '_blank', rel: 'noopener noreferrer'
+ = render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group, with_label: false
-- unless Gitlab::CurrentSettings.current_application_settings.hide_third_party_offers?
- = render 'personalize', f: f
+ - if Gitlab.config.mattermost.enabled
+ .row
+ = render 'create_chat_team', f: f
-.row.js-invite-members-section
- .col-sm-4
- = render_if_exists 'shared/groups/invite_members'
+ - unless Gitlab::CurrentSettings.current_application_settings.hide_third_party_offers?
+ = render 'personalize', f: f
-- if captcha_required?
- .row.recaptcha
+ .row.js-invite-members-section
.col-sm-4
- = recaptcha_tags nonce: content_security_policy_nonce
+ = render_if_exists 'shared/groups/invite_members'
+
+ - if captcha_required?
+ .row.recaptcha
+ .col-sm-4
+ = recaptcha_tags nonce: content_security_policy_nonce
.row
.col-sm-12
- = f.submit _('Create group'), class: "btn gl-button btn-confirm"
+ = f.submit submit_label, class: "btn gl-button btn-confirm", data: { qa_selector: 'create_group_button' }
= link_to _('Cancel'), dashboard_groups_path, class: 'btn gl-button btn-default btn-cancel'
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index 3fb2b88dadd..8384c906eeb 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -6,7 +6,8 @@
.group-edit-container.gl-mt-5
- .js-new-group-creation{ data: { has_errors: @group.errors.any?.to_s }.merge(verification_for_group_creation_data) }
+ .js-new-group-creation{ data: { has_errors: @group.errors.any?.to_s }.merge(subgroup_creation_data(@group),
+ verification_for_group_creation_data) }
.row{ 'v-cloak': true }
#create-group-pane.tab-pane
diff --git a/app/views/layouts/_google_tag_manager_head.html.haml b/app/views/layouts/_google_tag_manager_head.html.haml
index 25af51ca9cb..f5c823465be 100644
--- a/app/views/layouts/_google_tag_manager_head.html.haml
+++ b/app/views/layouts/_google_tag_manager_head.html.haml
@@ -1,4 +1,23 @@
- return unless google_tag_manager_enabled?
+- if Feature.enabled?(:gitlab_gtm_datalayer, type: :ops)
+ = javascript_tag do
+ :plain
+ window.dataLayer = window.dataLayer || [];
+ function gtag(){dataLayer.push(arguments);}
+
+ gtag('consent', 'default', {
+ 'analytics_storage': 'denied',
+ 'ad_storage': 'denied',
+ 'functionality_storage': 'denied',
+ 'region': ['EU', 'UK', 'PE', 'RU'],
+ 'wait_for_update': 500
+ });
+ gtag('consent', 'default', {
+ 'analytics_storage': 'granted',
+ 'ad_storage': 'granted',
+ 'functionality_storage': 'granted',
+ 'wait_for_update': 500
+ });
- if Feature.enabled?(:gtm_nonce, type: :ops)
= javascript_tag nonce: content_security_policy_nonce do
diff --git a/app/views/notify/user_auto_banned_email.html.haml b/app/views/notify/user_auto_banned_email.html.haml
deleted file mode 100644
index 8c33cd7299d..00000000000
--- a/app/views/notify/user_auto_banned_email.html.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-- link_start = '