Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-13 00:09:01 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-13 00:09:01 +0300
commiteef0c69d45082b370f1e41e50f12488a216944f2 (patch)
tree5c4a5c0e4db3fff89b9b4146b799e5de9dca57b9 /app
parent6d533fe8b44007d82b8de29a4b706da69e5f5936 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/pages/projects/clusters/new/index.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue6
-rw-r--r--app/assets/stylesheets/mailer.scss23
-rw-r--r--app/experiments/application_experiment.rb33
-rw-r--r--app/experiments/members/invite_email_experiment.rb14
-rw-r--r--app/experiments/strategy/round_robin.rb78
-rw-r--r--app/helpers/notify_helper.rb26
-rw-r--r--app/models/project.rb4
-rw-r--r--app/models/project_services/alerts_service.rb28
-rw-r--r--app/views/admin/users/_head.html.haml3
-rw-r--r--app/views/notify/member_invited_email.html.haml25
11 files changed, 188 insertions, 56 deletions
diff --git a/app/assets/javascripts/pages/projects/clusters/new/index.js b/app/assets/javascripts/pages/projects/clusters/new/index.js
index 876bab0b339..de9ded87ef3 100644
--- a/app/assets/javascripts/pages/projects/clusters/new/index.js
+++ b/app/assets/javascripts/pages/projects/clusters/new/index.js
@@ -1,5 +1,3 @@
import initNewCluster from '~/clusters/new_cluster';
-document.addEventListener('DOMContentLoaded', () => {
- initNewCluster();
-});
+initNewCluster();
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
index 8084ad59f42..8a7f726ad63 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
@@ -1,5 +1,5 @@
<script>
-/* eslint-disable vue/require-default-prop, vue/no-v-html */
+/* eslint-disable vue/require-default-prop */
import {
GlIcon,
GlLink,
@@ -7,6 +7,7 @@ import {
GlSprintf,
GlTooltip,
GlTooltipDirective,
+ GlSafeHtmlDirective,
} from '@gitlab/ui';
import mrWidgetPipelineMixin from 'ee_else_ce/vue_merge_request_widget/mixins/mr_widget_pipeline';
import { s__, n__ } from '~/locale';
@@ -32,6 +33,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
+ SafeHtml: GlSafeHtmlDirective,
},
mixins: [mrWidgetPipelineMixin],
props: {
@@ -201,10 +203,10 @@ export default {
<template v-if="showSourceBranch">
{{ s__('Pipeline|on') }}
<tooltip-on-truncate
+ v-safe-html="sourceBranchLink"
:title="sourceBranch"
truncate-target="child"
class="label-branch label-truncate gl-font-weight-normal"
- v-html="sourceBranchLink"
/>
</template>
</div>
diff --git a/app/assets/stylesheets/mailer.scss b/app/assets/stylesheets/mailer.scss
index 27c6ef20269..4f76deeb991 100644
--- a/app/assets/stylesheets/mailer.scss
+++ b/app/assets/stylesheets/mailer.scss
@@ -14,13 +14,15 @@ $mailer-line-cell-bg-color: #6b4fbb;
$mailer-wrapper-cell-bg-color: #fff;
$mailer-wrapper-cell-border-color: #ededed;
$mailer-header-footer-text-color: #5c5c5c;
+$full-width: 640px;
+$half-width: 320px;
body {
margin: 0 !important;
background-color: $mailer-bg-color;
padding: 0;
text-align: center;
- min-width: 640px;
+ min-width: $full-width;
width: 100%;
height: 100%;
font-family: $mailer-font;
@@ -31,7 +33,7 @@ table#body {
margin: 0;
padding: 0;
text-align: center;
- min-width: 640px;
+ min-width: $full-width;
width: 100%;
}
@@ -44,6 +46,11 @@ a {
}
}
+.mail-avatar {
+ border-radius: 50%;
+ display: block;
+}
+
.highlight {
font-weight: 500;
}
@@ -77,10 +84,18 @@ a {
margin-left: 4px;
}
+.half-width {
+ min-width: $half-width;
+}
+
tr td {
font-family: $mailer-font;
}
+tr.border-top td {
+ border-top: 2px solid $gray-100;
+}
+
tr.line td {
font-family: $mailer-font;
background-color: $mailer-line-cell-bg-color;
@@ -100,7 +115,7 @@ td.footer-message {
}
table.wrapper {
- width: 640px;
+ width: $full-width;
margin: 0 auto;
border-collapse: separate;
border-spacing: 0;
@@ -149,7 +164,7 @@ tr.footer td {
}
.gitlab-info-text {
- max-width: 640px;
+ max-width: $full-width;
margin: 0 auto;
text-align: center;
color: $gray-400;
diff --git a/app/experiments/application_experiment.rb b/app/experiments/application_experiment.rb
index 1c9a3c5f37f..663c58ceda6 100644
--- a/app/experiments/application_experiment.rb
+++ b/app/experiments/application_experiment.rb
@@ -23,16 +23,41 @@ class ApplicationExperiment < Gitlab::Experiment # rubocop:disable Gitlab/Namesp
))
end
+ def rollout_strategy
+ # no-op override in inherited class as desired
+ end
+
+ def variants
+ # override as desired in inherited class with all variants + control
+ # %i[variant1 variant2 control]
+ #
+ # this will make sure we supply variants as these go together - rollout_strategy of :round_robin must have variants
+ raise NotImplementedError, "Inheriting class must supply variants as an array if :round_robin strategy is used" if rollout_strategy == :round_robin
+ end
+
private
+ def feature_flag_name
+ name.tr('/', '_')
+ end
+
def resolve_variant_name
- return variant_names.first if Feature.enabled?(feature_flag_name, self, type: :experiment, default_enabled: :yaml)
+ case rollout_strategy
+ when :round_robin
+ round_robin_rollout
+ else
+ percentage_rollout
+ end
+ end
- nil # Returning nil vs. :control is important for not caching and rollouts.
+ def round_robin_rollout
+ Strategy::RoundRobin.new(feature_flag_name, variants).execute
end
- def feature_flag_name
- name.tr('/', '_')
+ def percentage_rollout
+ return variant_names.first if Feature.enabled?(feature_flag_name, self, type: :experiment, default_enabled: :yaml)
+
+ nil # Returning nil vs. :control is important for not caching and rollouts.
end
# Cache is an implementation on top of Gitlab::Redis::SharedState that also
diff --git a/app/experiments/members/invite_email_experiment.rb b/app/experiments/members/invite_email_experiment.rb
index 58703fd505d..4a03ebb7726 100644
--- a/app/experiments/members/invite_email_experiment.rb
+++ b/app/experiments/members/invite_email_experiment.rb
@@ -7,16 +7,12 @@ module Members
INVITE_TYPE = 'initial_email'
- private
+ def rollout_strategy
+ :round_robin
+ end
- def resolve_variant_name
- # we are overriding here so that when we add another experiment
- # we can merely add that variant and check of feature flag here
- if Feature.enabled?(feature_flag_name, self, type: :experiment, default_enabled: :yaml)
- :avatar
- else
- nil # :control
- end
+ def variants
+ %i[avatar permission_info control]
end
end
end
diff --git a/app/experiments/strategy/round_robin.rb b/app/experiments/strategy/round_robin.rb
new file mode 100644
index 00000000000..7b80c0e984d
--- /dev/null
+++ b/app/experiments/strategy/round_robin.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+module Strategy
+ class RoundRobin
+ CacheError = Class.new(StandardError)
+
+ COUNTER_EXPIRE_TIME = 86400 # one day
+
+ def initialize(key, variants)
+ @key = key
+ @variants = variants
+ end
+
+ def execute
+ increment_counter
+ resolve_variant_name
+ end
+
+ # When the counter would expire
+ #
+ # @api private Used internally by SRE and debugging purpose
+ # @return [Integer] Number in seconds until expiration or false if never
+ def counter_expires_in
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.ttl(key)
+ end
+ end
+
+ # Return the actual counter value
+ #
+ # @return [Integer] value
+ def counter_value
+ Gitlab::Redis::SharedState.with do |redis|
+ (redis.get(key) || 0).to_i
+ end
+ end
+
+ # Reset the counter
+ #
+ # @private Used internally by SRE and debugging purpose
+ # @return [Boolean] whether reset was a success
+ def reset!
+ redis_cmd do |redis|
+ redis.del(key)
+ end
+ end
+
+ private
+
+ attr_reader :key, :variants
+
+ # Increase the counter
+ #
+ # @return [Boolean] whether operation was a success
+ def increment_counter
+ redis_cmd do |redis|
+ redis.incr(key)
+ redis.expire(key, COUNTER_EXPIRE_TIME)
+ end
+ end
+
+ def resolve_variant_name
+ remainder = counter_value % variants.size
+
+ variants[remainder]
+ end
+
+ def redis_cmd
+ Gitlab::Redis::SharedState.with { |redis| yield(redis) }
+
+ true
+ rescue CacheError => e
+ Gitlab::AppLogger.warn("GitLab: An unexpected error occurred in writing to Redis: #{e}")
+
+ false
+ end
+ end
+end
diff --git a/app/helpers/notify_helper.rb b/app/helpers/notify_helper.rb
index db7527d9d58..03da679cfdd 100644
--- a/app/helpers/notify_helper.rb
+++ b/app/helpers/notify_helper.rb
@@ -8,4 +8,30 @@ module NotifyHelper
def issue_reference_link(entity, *args, full: false)
link_to(entity.to_reference(full: full), issue_url(entity, *args))
end
+
+ def invited_role_description(role_name)
+ case role_name
+ when "Guest"
+ s_("InviteEmail|As a guest, you can view projects, leave comments, and create issues.")
+ when "Reporter"
+ s_("InviteEmail|As a reporter, you can view projects and reports, and leave comments on issues.")
+ when "Developer"
+ s_("InviteEmail|As a developer, you have full access to projects, so you can take an idea from concept to production.")
+ when "Maintainer"
+ s_("InviteEmail|As a maintainer, you have full access to projects. You can push commits to master and deploy to production.")
+ when "Owner"
+ s_("InviteEmail|As an owner, you have full access to projects and can manage access to the group, including inviting new members.")
+ when "Minimal Access"
+ s_("InviteEmail|As a user with minimal access, you can view the high-level group from the UI and API.")
+ end
+ end
+
+ def invited_to_description(source)
+ case source
+ when "project"
+ s_('InviteEmail|Projects can be used to host your code, track issues, collaborate on code, and continuously build, test, and deploy your app with built-in GitLab CI/CD.')
+ when "group"
+ s_('InviteEmail|Groups assemble related projects together and grant members access to several projects at once.')
+ end
+ end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index b8ca8a744c8..2b9b7dcf733 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1351,9 +1351,9 @@ class Project < ApplicationRecord
end
def disabled_services
- return %w(datadog alerts) unless Feature.enabled?(:datadog_ci_integration, self)
+ return %w(datadog) unless Feature.enabled?(:datadog_ci_integration, self)
- %w(alerts)
+ []
end
def find_or_initialize_service(name)
diff --git a/app/models/project_services/alerts_service.rb b/app/models/project_services/alerts_service.rb
deleted file mode 100644
index 4afce0dfe95..00000000000
--- a/app/models/project_services/alerts_service.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# frozen_string_literal: true
-
-# This service is scheduled for removal. All records must
-# be deleted before the class can be removed.
-# https://gitlab.com/groups/gitlab-org/-/epics/5056
-class AlertsService < Service
- before_save :prevent_save
-
- def self.to_param
- 'alerts'
- end
-
- def self.supported_events
- %w()
- end
-
- private
-
- def prevent_save
- errors.add(:base, _('Alerts endpoint is deprecated and should not be created or modified. Use HTTP Integrations instead.'))
- log_error('Prevented attempt to save or update deprecated AlertsService')
-
- # Stops execution of callbacks and database operation while
- # preserving expectations of #save (will not raise) & #save! (raises)
- # https://guides.rubyonrails.org/active_record_callbacks.html#halting-execution
- throw :abort # rubocop:disable Cop/BanCatchThrow
- end
-end
diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml
index 554f7470694..2f26fbdf1e1 100644
--- a/app/views/admin/users/_head.html.haml
+++ b/app/views/admin/users/_head.html.haml
@@ -6,6 +6,9 @@
- elsif @user.blocked?
%span.cred
= s_('AdminUsers|(Blocked)')
+ - if @user.group_managed_account?
+ %span.cred
+ = s_('AdminUsers|(Group Managed Account)')
- if @user.internal?
%span.cred
= s_('AdminUsers|(Internal)')
diff --git a/app/views/notify/member_invited_email.html.haml b/app/views/notify/member_invited_email.html.haml
index 55251fe88de..f7dc1fa662c 100644
--- a/app/views/notify/member_invited_email.html.haml
+++ b/app/views/notify/member_invited_email.html.haml
@@ -1,7 +1,7 @@
- placeholders = { strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe, project_or_group_name: member_source.human_name, project_or_group: member_source.model_name.singular, br_tag: '<br/>'.html_safe, role: member.human_access.downcase }
-- experiment('members/invite_email', actor: member) do |e|
- - e.use do
+- experiment('members/invite_email', actor: member) do |experiment_instance|
+ - experiment_instance.use do
%tr
%td.text-content
%h2.invite-header
@@ -13,11 +13,28 @@
= html_escape(s_("InviteEmail|You are invited to join the %{strong_start}%{project_or_group_name}%{strong_end}%{br_tag}%{project_or_group} as a %{role}")) % placeholders
%p.invite-actions
= link_to s_('InviteEmail|Join now'), invite_url(@token, invite_type: Members::InviteEmailExperiment::INVITE_TYPE), class: 'invite-btn-join'
- - e.try(:avatar) do
+ - experiment_instance.try(:avatar) do
%tr
%td.text-content
- %img.avatar{ height: "60", src: avatar_icon_for_user(member.created_by, 60, only_path: false), style: "display: block; border-radius: 30px; margin: -2px 0;", width: "60", alt: "" }
+ %img.mail-avatar{ height: "60", src: avatar_icon_for_user(member.created_by, 60, only_path: false), width: "60", alt: "" }
%p
= html_escape(s_("InviteEmail|%{inviter} invited you to join the %{strong_start}%{project_or_group_name}%{strong_end}%{br_tag}%{project_or_group} as a %{role}")) % placeholders.merge({ inviter: (link_to member.created_by.name, user_url(member.created_by)).html_safe })
%p.invite-actions
= link_to s_('InviteEmail|Join now'), invite_url(@token, invite_type: Members::InviteEmailExperiment::INVITE_TYPE), class: 'invite-btn-join'
+ - experiment_instance.try(:permission_info) do
+ %tr
+ %td.text-content{ colspan: 2 }
+ %img.mail-avatar{ height: "60", src: avatar_icon_for_user(member.created_by, 60, only_path: false), width: "60", alt: "" }
+ %p
+ = html_escape(s_("InviteEmail|%{inviter} invited you to join the %{strong_start}%{project_or_group_name}%{strong_end}%{br_tag}%{project_or_group} with the %{role} permission level.")) % placeholders.merge({ inviter: (link_to member.created_by.name, user_url(member.created_by)).html_safe })
+ %p.invite-actions
+ = link_to s_('InviteEmail|Join now'), invite_url(@token, invite_type: Members::InviteEmailExperiment::INVITE_TYPE), class: 'invite-btn-join'
+ %tr.border-top
+ %td.text-content.half-width
+ %h4
+ = s_('InviteEmail|What is a GitLab %{project_or_group}?') % { project_or_group: member_source.model_name.singular }
+ %p= invited_to_description(member_source.model_name.singular)
+ %td.text-content.half-width
+ %h4
+ = s_('InviteEmail|What can I do with the %{role} permission level?') % { role: member.human_access.downcase }
+ %p= invited_role_description(member.human_access)