diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-13 00:09:01 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-13 00:09:01 +0300 |
commit | eef0c69d45082b370f1e41e50f12488a216944f2 (patch) | |
tree | 5c4a5c0e4db3fff89b9b4146b799e5de9dca57b9 /app | |
parent | 6d533fe8b44007d82b8de29a4b706da69e5f5936 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/pages/projects/clusters/new/index.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue | 6 | ||||
-rw-r--r-- | app/assets/stylesheets/mailer.scss | 23 | ||||
-rw-r--r-- | app/experiments/application_experiment.rb | 33 | ||||
-rw-r--r-- | app/experiments/members/invite_email_experiment.rb | 14 | ||||
-rw-r--r-- | app/experiments/strategy/round_robin.rb | 78 | ||||
-rw-r--r-- | app/helpers/notify_helper.rb | 26 | ||||
-rw-r--r-- | app/models/project.rb | 4 | ||||
-rw-r--r-- | app/models/project_services/alerts_service.rb | 28 | ||||
-rw-r--r-- | app/views/admin/users/_head.html.haml | 3 | ||||
-rw-r--r-- | app/views/notify/member_invited_email.html.haml | 25 |
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) |