Commit 7ccae46f authored by Vitaly Slobodin's avatar Vitaly Slobodin Committed by Bob Van Landuyt

Notify self-managed admins about active user count

Closes https://gitlab.com/gitlab-org/gitlab/-/issues/217804/
parent 5111b545
......@@ -36,7 +36,7 @@ import initSearchAutocomplete from './search_autocomplete';
import GlFieldErrors from './gl_field_errors';
import initUserPopovers from './user_popovers';
import initBroadcastNotifications from './broadcast_notification';
import PersistentUserCallout from './persistent_user_callout';
import initPersistentUserCallouts from './persistent_user_callouts';
import { initUserTracking } from './tracking';
import { __ } from './locale';
......@@ -108,12 +108,7 @@ function deferredInitialisation() {
initUserPopovers();
initBroadcastNotifications();
initFrequentItemDropdowns();
const recoverySettingsCallout = document.querySelector('.js-recovery-settings-callout');
PersistentUserCallout.factory(recoverySettingsCallout);
const usersOverLicenseCallout = document.querySelector('.js-users-over-license-callout');
PersistentUserCallout.factory(usersOverLicenseCallout);
initPersistentUserCallouts();
if (document.querySelector('.search')) initSearchAutocomplete();
......
import PersistentUserCallout from './persistent_user_callout';
const PERSISTENT_USER_CALLOUTS = [
'.js-recovery-settings-callout',
'.js-users-over-license-callout',
'.js-admin-licensed-user-count-threshold',
];
const initCallouts = () => {
PERSISTENT_USER_CALLOUTS.forEach(calloutContainer =>
PersistentUserCallout.factory(document.querySelector(calloutContainer)),
);
};
export default initCallouts;
......@@ -8,6 +8,7 @@
= render_if_exists 'layouts/header/users_over_license_banner'
- if Feature.enabled?(:subscribable_banner_license, default_enabled: true)
= render_if_exists "layouts/header/ee_subscribable_banner"
= render_if_exists "layouts/header/licensed_user_count_threshold"
= render "layouts/broadcast"
= render "layouts/header/read_only_banner"
= render "layouts/nav/classification_level_banner"
......
......@@ -13,6 +13,7 @@ module EE
ACCOUNT_RECOVERY_REGULAR_CHECK = 'account_recovery_regular_check'
USERS_OVER_LICENSE_BANNER = 'users_over_license_banner'
STANDALONE_VULNERABILITIES_INTRODUCTION_BANNER = 'standalone_vulnerabilities_introduction_banner'
ACTIVE_USER_COUNT_THRESHOLD = 'active_user_count_threshold'
def show_canary_deployment_callout?(project)
!user_dismissed?(CANARY_DEPLOYMENT) &&
......
......@@ -3,10 +3,24 @@
module LicenseMonitoringHelper
include Gitlab::Utils::StrongMemoize
ACTIVE_USER_COUNT_THRESHOLD_LEVELS = [
{ range: (2..15), percentage: false, value: 1 },
{ range: (16..25), percentage: false, value: 2 },
{ range: (26..99), percentage: true, value: 10 },
{ range: (100..999), percentage: true, value: 8 },
{ range: (1000..nil), percentage: true, value: 5 }
].freeze
def show_users_over_license_banner?
current_user&.admin? && license_is_over_capacity?
end
def show_active_user_count_threshold_banner?
return if ::Gitlab.com?
current_user&.admin? && active_user_count_threshold_reached?
end
private
def license_is_over_capacity?
......@@ -15,6 +29,17 @@ module LicenseMonitoringHelper
current_license_overage > 0
end
def active_user_count_threshold_reached?
return if current_license.nil? || current_license.trial?
return if total_user_count.nil? || total_user_count == 1
active_user_count_threshold[:value] >= if active_user_count_threshold[:percentage]
remaining_user_count.fdiv(total_user_count) * 100
else
remaining_user_count
end
end
def current_license
strong_memoize(:current_license) { License.current }
end
......@@ -22,4 +47,24 @@ module LicenseMonitoringHelper
def current_license_overage
strong_memoize(:current_license_overage) { current_license.overage_with_historical_max }
end
def current_active_users_count
strong_memoize(:current_active_users_count) { current_license.current_active_users_count }
end
def total_user_count
strong_memoize(:total_user_count) { current_license.restricted_user_count }
end
def remaining_user_count
strong_memoize(:remaining_user_count) { total_user_count - current_active_users_count }
end
def active_user_count_threshold
strong_memoize(:active_user_count_threshold) do
ACTIVE_USER_COUNT_THRESHOLD_LEVELS.find do |threshold|
threshold[:range].include?(total_user_count)
end
end
end
end
......@@ -18,7 +18,8 @@ module EE
threat_monitoring_info: 11,
account_recovery_regular_check: 12,
users_over_license_banner: 16,
standalone_vulnerabilities_introduction_banner: 17
standalone_vulnerabilities_introduction_banner: 17,
active_user_count_threshold: 18
)
end
end
......
- return unless show_active_user_count_threshold_banner?
.container-fluid.container-limited.pt-3
.gl-alert.gl-alert-info.gitlab-ee-license-banner.alert-primary.js-admin-licensed-user-count-threshold{ role: 'alert', data: { feature_id: UserCalloutsHelper::ACTIVE_USER_COUNT_THRESHOLD, dismiss_endpoint: user_callouts_path } }
= sprite_icon('information-o', size: 16, css_class: 'gl-icon gl-alert-icon')
%button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss') }
= sprite_icon('close', size: 16, css_class: 'gl-icon')
.gl-alert-body
%h4.gl-alert-title= _('Your instance is approaching its licensed user count')
= _('Your instance has %{remaining_user_count} users remaining of the %{total_user_count} included in your subscription. You can add more users than the number included in your license, and we will include the overage in your next bill.') % { remaining_user_count: remaining_user_count, total_user_count: total_user_count }
.gl-alert-actions
= link_to _('View users statistics'), admin_users_path, class: 'btn gl-alert-action btn-info btn-md gl-button'
= link_to _('Contact support'), EE::CUSTOMER_LICENSE_SUPPORT_URL, rel: 'nofollow', class: 'btn gl-alert-action btn-info btn-md btn-secondary gl-button'
---
title: Add callout for user count threshold
merge_request: 32404
author:
type: added
......@@ -8,4 +8,5 @@ module EE
SUBSCRIPTION_PORTAL_ADMIN_EMAIL = ENV.fetch('SUBSCRIPTION_PORTAL_ADMIN_EMAIL', 'gl_com_api@gitlab.com')
SUBSCRIPTION_PORTAL_ADMIN_TOKEN = ENV.fetch('SUBSCRIPTION_PORTAL_ADMIN_TOKEN', 'customer_admin_token')
CUSTOMER_SUPPORT_URL = 'https://support.gitlab.com'.freeze
CUSTOMER_LICENSE_SUPPORT_URL = 'https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293'.freeze
end
# frozen_string_literal: true
require 'spec_helper'
describe 'Display approaching user count limit banner', :js do
let_it_be(:admin) { create(:admin) }
let_it_be(:user) { create(:user) }
let_it_be(:license_seats_limit) { 10 }
let_it_be(:license) do
create(:license, data: build(:gitlab_license, restrictions: { active_user_count: license_seats_limit }).export)
end
shared_examples_for 'a visible banner' do
it 'shows the banner' do
visit root_dashboard_path
expect(page).to have_content('Your instance is approaching its licensed user count')
expect(page).to have_link('View users statistics', href: admin_users_path)
expect(page).to have_link('Contact support', href: EE::CUSTOMER_LICENSE_SUPPORT_URL)
end
end
shared_examples_for 'a hidden banner' do
it 'does not show the banner' do
visit root_dashboard_path
expect(page).not_to have_content('Your instance is approaching its licensed user count')
expect(page).not_to have_link('View users statistics', href: admin_users_path)
expect(page).not_to have_link('Contact support', href: EE::CUSTOMER_LICENSE_SUPPORT_URL)
end
end
context 'with reached user count threshold' do
before do
# +1 created admin on line 8
# +1 created user on line 9
create_list(:user, 7)
end
context 'when admin is logged in' do
before do
gitlab_sign_in(admin)
end
it_behaves_like 'a visible banner'
end
context 'when regular user is logged in' do
before do
gitlab_sign_in(user)
end
it_behaves_like 'a hidden banner'
end
end
context 'when not reached user count threshold' do
let(:active_user_count) { 1 }
context 'when admin is logged in' do
before do
gitlab_sign_in(admin)
end
it_behaves_like 'a hidden banner'
end
context 'when regular user is logged in' do
before do
gitlab_sign_in(user)
end
it_behaves_like 'a hidden banner'
end
end
context 'without license' do
before do
allow(License).to receive(:current).and_return(nil)
end
it_behaves_like 'a hidden banner'
end
context 'with trial license' do
before do
allow(License).to receive(:trial?).and_return(true)
end
it_behaves_like 'a hidden banner'
end
end
......@@ -72,4 +72,68 @@ describe LicenseMonitoringHelper do
end
end
end
describe '#show_active_user_count_threshold_banner?' do
let_it_be(:current_active_users_count) { 1 }
subject { helper.show_active_user_count_threshold_banner? }
before do
allow(helper).to receive(:current_active_users_count).and_return(current_active_users_count)
end
context 'when admin user is logged in' do
before do
allow(helper).to receive(:current_user).and_return(admin)
end
context 'when active users count is above the threshold' do
let(:current_active_users_count) { license_seats_limit - 1 }
it { is_expected.to be_truthy }
end
context 'when active users count is below the threshold' do
let(:current_active_users_count) { 1 }
it { is_expected.to be_falsey }
end
end
context 'when regular user is logged in' do
before do
allow(helper).to receive(:current_user).and_return(user)
end
context 'when active users count is above the threshold' do
let(:current_active_users_count) { license_seats_limit - 1 }
it { is_expected.to be_falsey }
end
context 'when active users count is below the threshold' do
let(:current_active_users_count) { 1 }
it { is_expected.to be_falsey }
end
end
context 'with anonymous user' do
before do
allow(helper).to receive(:current_user).and_return(nil)
end
context 'when active users count is above the threshold' do
let(:current_active_users_count) { license_seats_limit - 1 }
it { is_expected.to be_falsey }
end
context 'when active users count is below the threshold' do
let(:current_active_users_count) { 1 }
it { is_expected.to be_falsey }
end
end
end
end
......@@ -24430,6 +24430,9 @@ msgstr ""
msgid "View the performance dashboard at"
msgstr ""
msgid "View users statistics"
msgstr ""
msgid "Viewing commit"
msgstr ""
......@@ -25638,6 +25641,12 @@ msgstr ""
msgid "Your groups"
msgstr ""
msgid "Your instance has %{remaining_user_count} users remaining of the %{total_user_count} included in your subscription. You can add more users than the number included in your license, and we will include the overage in your next bill."
msgstr ""
msgid "Your instance is approaching its licensed user count"
msgstr ""
msgid "Your issues are being imported. Once finished, you'll get a confirmation email."
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