Commit c4f46faa authored by Illya Klymov's avatar Illya Klymov

Merge branch '352642-notify-free-plans-when-over-limit' into 'master'

Add reached limit warning for free namespaces

See merge request gitlab-org/gitlab!84016
parents b89ef59e 6a3ddd2f
......@@ -15,7 +15,8 @@ module Users
storage_enforcement_banner_second_enforcement_threshold: 4,
storage_enforcement_banner_third_enforcement_threshold: 5,
storage_enforcement_banner_fourth_enforcement_threshold: 6,
preview_user_over_limit_free_plan_alert: 7 # EE-only
preview_user_over_limit_free_plan_alert: 7, # EE-only
user_reached_limit_free_plan_alert: 8 # EE-only
}
validates :group, presence: true
......
......@@ -6,14 +6,23 @@ module EE
extend ::Gitlab::Utils::Override
PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT = 'preview_user_over_limit_free_plan_alert'
USER_REACHED_LIMIT_FREE_PLAN_ALERT = 'user_reached_limit_free_plan_alert'
def show_user_over_limit_free_plan_alert?(namespace)
def show_preview_user_over_limit_free_plan_alert?(namespace)
return false if namespace.user_namespace?
return false if user_dismissed_for_group(PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT, namespace, 14.days.ago)
return false unless Ability.allowed?(current_user, :owner_access, namespace)
::Namespaces::PreviewFreeUserCap.new(namespace).over_limit?
end
def show_user_reached_limit_free_plan_alert?(namespace)
return false if namespace.user_namespace?
return false if user_dismissed_for_group(USER_REACHED_LIMIT_FREE_PLAN_ALERT, namespace)
return false unless Ability.allowed?(current_user, :owner_access, namespace)
::Namespaces::FreeUserCap.new(namespace).reached_limit?
end
end
end
end
- return unless show_user_over_limit_free_plan_alert?(source.root_ancestor)
- content_for :user_over_limit_free_plan_alert do
.container-fluid.container-limited{ class: "gl-pb-2! gl-pt-6! #{@content_class}" }
= render Pajamas::AlertComponent.new(alert_class: 'js-user-over-limit-free-plan-alert',
alert_data: { track_action: 'render',
track_label: 'user_limit_banner',
dismiss_endpoint: group_callouts_path,
feature_id: Users::GroupCalloutsHelper::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT,
group_id: source.root_ancestor.id,
testid: 'user-over-limit-free-plan-alert' },
title: _('From June 22, 2022 (GitLab 15.1), free personal namespaces and top-level groups will be limited to %{free_limit} members') % { free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT },
close_button_data: { track_action: 'dismiss_banner',
track_label: 'user_limit_banner',
testid: 'user-over-limit-free-plan-dismiss' }) do
.gl-alert-body
= _('Your %{doc_link_start}namespace%{doc_link_end}, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_limit} members. From June 22, 2022, it will be limited to %{free_limit}, and the remaining members will get a %{link_start}status of Over limit%{link_end} and lose access to the namespace. You can go to the Usage Quotas page to manage which %{free_limit} members will remain in your namespace. To get more members, an owner can start a trial or upgrade to a paid tier.').html_safe % { namespace_name: source.root_ancestor.name,
free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT,
doc_link_start: '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/index', anchor: 'namespaces') },
doc_link_end: '</a>'.html_safe,
- if show_user_reached_limit_free_plan_alert?(source.root_ancestor)
.container-fluid.container-limited{ class: "gl-pb-2! gl-pt-6! #{@content_class}" }
= render Pajamas::AlertComponent.new(alert_class: 'js-user-over-limit-free-plan-alert',
variant: :warning,
alert_data: { track_action: 'render',
track_label: 'user_limit_banner',
dismiss_endpoint: group_callouts_path,
feature_id: Users::GroupCalloutsHelper::USER_REACHED_LIMIT_FREE_PLAN_ALERT,
group_id: source.root_ancestor.id,
testid: 'user-over-limit-free-plan-alert' },
title: _("Looks like you've reached your %{free_limit} member limit for %{strong_start}%{namespace_name}%{strong_end}").html_safe % { free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT,
strong_start: "<strong>".html_safe,
strong_end: "</strong>".html_safe,
link_start: '<a href="https://about.gitlab.com/blog/2022/03/24/efficient-free-tier" target="_blank" rel="noopener noreferrer">'.html_safe,
link_end: '</a>'.html_safe }
.gl-alert-actions
= link_to _('Manage members'),
group_usage_quotas_path(source.root_ancestor),
class: 'btn gl-alert-action btn-info btn-md gl-button',
data: { track_action: 'click_button',
track_label: 'manage_members',
testid: 'user-over-limit-free-plan-manage' }
= link_to _('Explore paid plans'),
group_billings_path(source.root_ancestor),
class: 'btn gl-alert-action btn-default btn-md gl-button',
data: { track_action: 'click_button',
track_label: 'explore_paid_plans',
testid: 'user-over-limit-free-plan-explore' }
namespace_name: source.root_ancestor.name },
close_button_data: { track_action: 'dismiss_banner',
track_label: 'user_limit_banner',
testid: 'user-over-limit-free-plan-dismiss' }) do
.gl-alert-body
= _("You can't add any more, but you can manage your existing members, for example, by removing inactive members and replacing them with new members. To get more members an owner of this namespace can start a trial or upgrade to a paid tier.")
.gl-alert-actions
= link_to _('Manage members'),
group_usage_quotas_path(source.root_ancestor),
class: 'btn gl-alert-action btn-info btn-md gl-button',
data: { track_action: 'click_button',
track_label: 'manage_members',
testid: 'user-over-limit-free-plan-manage' }
= link_to _('Explore paid plans'),
group_billings_path(source.root_ancestor),
class: 'btn gl-alert-action btn-default btn-md gl-button',
data: { track_action: 'click_button',
track_label: 'explore_paid_plans',
testid: 'user-over-limit-free-plan-explore' }
- elsif show_preview_user_over_limit_free_plan_alert?(source.root_ancestor)
.container-fluid.container-limited{ class: "gl-pb-2! gl-pt-6! #{@content_class}" }
= render Pajamas::AlertComponent.new(alert_class: 'js-user-over-limit-free-plan-alert',
alert_data: { track_action: 'render',
track_label: 'user_limit_banner',
dismiss_endpoint: group_callouts_path,
feature_id: Users::GroupCalloutsHelper::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT,
group_id: source.root_ancestor.id,
testid: 'user-over-limit-free-plan-alert' },
title: _('From June 22, 2022 (GitLab 15.1), free personal namespaces and top-level groups will be limited to %{free_limit} members') % { free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT },
close_button_data: { track_action: 'dismiss_banner',
track_label: 'user_limit_banner',
testid: 'user-over-limit-free-plan-dismiss' }) do
.gl-alert-body
= _('Your %{doc_link_start}namespace%{doc_link_end}, %{strong_start}%{namespace_name}%{strong_end} has more than %{free_limit} members. From June 22, 2022, it will be limited to %{free_limit}, and the remaining members will get a %{link_start}status of Over limit%{link_end} and lose access to the namespace. You can go to the Usage Quotas page to manage which %{free_limit} members will remain in your namespace. To get more members, an owner can start a trial or upgrade to a paid tier.').html_safe % { namespace_name: source.root_ancestor.name,
free_limit: ::Namespaces::FreeUserCap::FREE_USER_LIMIT,
doc_link_start: '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/index', anchor: 'namespaces') },
doc_link_end: '</a>'.html_safe,
strong_start: "<strong>".html_safe,
strong_end: "</strong>".html_safe,
link_start: '<a href="https://about.gitlab.com/blog/2022/03/24/efficient-free-tier" target="_blank" rel="noopener noreferrer">'.html_safe,
link_end: '</a>'.html_safe }
.gl-alert-actions
= link_to _('Manage members'),
group_usage_quotas_path(source.root_ancestor),
class: 'btn gl-alert-action btn-info btn-md gl-button',
data: { track_action: 'click_button',
track_label: 'manage_members',
testid: 'user-over-limit-free-plan-manage' }
= link_to _('Explore paid plans'),
group_billings_path(source.root_ancestor),
class: 'btn gl-alert-action btn-default btn-md gl-button',
data: { track_action: 'click_button',
track_label: 'explore_paid_plans',
testid: 'user-over-limit-free-plan-explore' }
......@@ -10,10 +10,10 @@ RSpec.describe EE::Users::GroupCalloutsHelper do
allow(helper).to receive(:current_user).and_return(user)
end
describe '.show_user_over_limit_free_plan_alert?' do
describe '.show_preview_user_over_limit_free_plan_alert?' do
let(:preview_free_user_cap_over?) { true }
subject { helper.show_user_over_limit_free_plan_alert?(group) }
subject { helper.show_preview_user_over_limit_free_plan_alert?(group) }
before do
allow_next_instance_of(::Namespaces::PreviewFreeUserCap) do |preview_free_user_cap|
......@@ -73,4 +73,56 @@ RSpec.describe EE::Users::GroupCalloutsHelper do
it { is_expected.to eq(false) }
end
end
describe '.show_user_reached_limit_free_plan_alert?' do
let(:free_user_cap_reached?) { true }
subject { helper.show_user_reached_limit_free_plan_alert?(group) }
before do
allow_next_instance_of(::Namespaces::FreeUserCap) do |preview_free_user_cap|
allow(preview_free_user_cap).to receive(:reached_limit?).and_return(free_user_cap_reached?)
end
end
context 'when it is a group namespace' do
context 'when user has the owner_access ability for the group' do
before do
group.add_owner(user)
end
context 'when the invite_members_banner has not been dismissed' do
it { is_expected.to eq(true) }
context 'when free_user_cap_reached? is false' do
let(:free_user_cap_reached?) { false }
it { is_expected.to eq(false) }
end
end
context 'when the preview_user_over_limit_free_plan_alert has been dismissed' do
before do
create(:group_callout,
user: user,
group: group,
feature_name: described_class::USER_REACHED_LIMIT_FREE_PLAN_ALERT,
dismissed_at: Time.now)
end
it { is_expected.to eq(false) }
end
end
context 'when user does not have owner_access ability for the group' do
it { is_expected.to eq(false) }
end
end
context 'when it is a user_namespace' do
let_it_be(:group) { user.namespace }
it { is_expected.to eq(false) }
end
end
end
......@@ -3,33 +3,55 @@
RSpec.shared_examples_for 'over the free user limit alert' do
before do
stub_ee_application_setting(should_check_namespace_plan: true)
stub_feature_flags(free_user_cap: false)
stub_feature_flags(preview_free_user_cap: true)
stub_const('::Namespaces::FreeUserCap::FREE_USER_LIMIT', 1)
end
it 'shows free user limit warning and honors dismissal', :js do
alert_title_content = 'From June 22, 2022 (GitLab 15.1), free personal namespaces and top-level groups will be limited to'
shared_examples 'performs entire show dismiss cycle' do
it 'shows free user limit warning and honors dismissal', :js do
visit_page
visit_page
expect(page).not_to have_content(alert_title_content)
expect(page).not_to have_content(alert_title_content)
group.add_developer(create(:user))
group.add_developer(create(:user))
page.refresh
page.refresh
expect(page).to have_content(alert_title_content)
expect(page).to have_content(alert_title_content)
page.within('[data-testid="user-over-limit-free-plan-alert"]') do
expect(page).to have_link('Manage members')
expect(page).to have_link('Explore paid plans')
end
page.within('[data-testid="user-over-limit-free-plan-alert"]') do
expect(page).to have_link('Manage members')
expect(page).to have_link('Explore paid plans')
find('[data-testid="user-over-limit-free-plan-dismiss"]').click
page.refresh
expect(page).not_to have_content(alert_title_content)
end
end
context 'when over limit for preview' do
before do
stub_feature_flags(free_user_cap: false)
stub_feature_flags(preview_free_user_cap: true)
stub_const('::Namespaces::FreeUserCap::FREE_USER_LIMIT', 1)
end
find('[data-testid="user-over-limit-free-plan-dismiss"]').click
let(:alert_title_content) do
'From June 22, 2022 (GitLab 15.1), free personal namespaces and top-level groups will be limited to'
end
it_behaves_like 'performs entire show dismiss cycle'
end
context 'when reached/over limit' do
before do
stub_feature_flags(free_user_cap: true)
stub_const('::Namespaces::FreeUserCap::FREE_USER_LIMIT', 2)
end
visit visit_page
let(:alert_title_content) { "Looks like you've reached your" }
expect(page).not_to have_content(alert_title_content)
it_behaves_like 'performs entire show dismiss cycle'
end
end
......@@ -6,35 +6,64 @@ RSpec.describe 'shared/user_over_limit_free_plan_alert' do
let_it_be(:source) { create(:group) }
let(:partial) { 'shared/user_over_limit_free_plan_alert' }
let(:show_user_reached_limit_free_plan_alert) { false }
let(:show_preview_user_over_limit_free_plan_alert) { false }
before do
allow(view).to receive(:source).and_return(source)
allow(view).to receive(:show_user_over_limit_free_plan_alert?).with(source).and_return(true)
allow(view).to receive(:show_user_reached_limit_free_plan_alert?).with(source).and_return(show_user_reached_limit_free_plan_alert)
allow(view).to receive(:show_preview_user_over_limit_free_plan_alert?).with(source).and_return(show_preview_user_over_limit_free_plan_alert)
end
it 'renders all the expected tracking items', :aggregate_failures do
render partial
shared_examples 'limit tracking settings' do
it 'renders all the expected tracking items', :aggregate_failures do
render partial
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css('.js-user-over-limit-free-plan-alert[data-track-action="render"][data-track-label="user_limit_banner"]')
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css('[data-testid="user-over-limit-free-plan-dismiss"][data-track-action="dismiss_banner"][data-track-label="user_limit_banner"]')
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css('[data-testid="user-over-limit-free-plan-manage"][data-track-action="click_button"][data-track-label="manage_members"]')
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css('[data-testid="user-over-limit-free-plan-explore"][data-track-action="click_button"][data-track-label="explore_paid_plans"]')
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css('.js-user-over-limit-free-plan-alert[data-track-action="render"][data-track-label="user_limit_banner"]')
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css('[data-testid="user-over-limit-free-plan-dismiss"][data-track-action="dismiss_banner"][data-track-label="user_limit_banner"]')
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css('[data-testid="user-over-limit-free-plan-manage"][data-track-action="click_button"][data-track-label="manage_members"]')
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css('[data-testid="user-over-limit-free-plan-explore"][data-track-action="click_button"][data-track-label="explore_paid_plans"]')
end
end
it 'renders all the correct links and buttons', :aggregate_failures do
render partial
context 'when over limit for preview' do
let(:show_preview_user_over_limit_free_plan_alert) { true }
it_behaves_like 'limit tracking settings'
it 'renders all the correct links and buttons', :aggregate_failures do
render partial
expect_buttons_to_be_present
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_link('status of Over limit', href: 'https://about.gitlab.com/blog/2022/03/24/efficient-free-tier')
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css("[data-testid='user-over-limit-free-plan-alert'][data-dismiss-endpoint='#{group_callouts_path}'][data-feature-id='#{Users::GroupCalloutsHelper::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT}'][data-group-id='#{source.id}']")
end
end
context 'when reached limit' do
let(:show_user_reached_limit_free_plan_alert) { true }
it_behaves_like 'limit tracking settings'
it 'renders all the correct links and buttons', :aggregate_failures do
render partial
expect_buttons_to_be_present
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css("[data-testid='user-over-limit-free-plan-alert'][data-dismiss-endpoint='#{group_callouts_path}'][data-feature-id='#{Users::GroupCalloutsHelper::USER_REACHED_LIMIT_FREE_PLAN_ALERT}'][data-group-id='#{source.id}']")
end
end
def expect_buttons_to_be_present
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_link('Manage members', href: group_usage_quotas_path(source))
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_link('Explore paid plans', href: group_billings_path(source))
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_link('status of Over limit', href: 'https://about.gitlab.com/blog/2022/03/24/efficient-free-tier')
expect(view.content_for(:user_over_limit_free_plan_alert))
.to have_css("[data-testid='user-over-limit-free-plan-alert'][data-dismiss-endpoint='#{group_callouts_path}'][data-feature-id='#{Users::GroupCalloutsHelper::PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT}'][data-group-id='#{source.id}']")
end
end
......@@ -22832,6 +22832,9 @@ msgstr ""
msgid "Logs|To see the logs, deploy your code to an environment."
msgstr ""
msgid "Looks like you've reached your %{free_limit} member limit for %{strong_start}%{namespace_name}%{strong_end}"
msgstr ""
msgid "Low vulnerabilities present"
msgstr ""
......@@ -43025,6 +43028,9 @@ msgstr ""
msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}"
msgstr ""
msgid "You can't add any more, but you can manage your existing members, for example, by removing inactive members and replacing them with new members. To get more members an owner of this namespace can start a trial or upgrade to a paid tier."
msgstr ""
msgid "You cannot %{action} %{state} users."
msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment