From eb9ac6e66e76b5a9422c3780f0106300ad265bab Mon Sep 17 00:00:00 2001
From: Aishwarya Subramanian <asubramanian@gitlab.com>
Date: Wed, 12 Jun 2024 13:03:30 +0000
Subject: [PATCH] Display temporary extension expiration banner

Display temporary extension banner when
the license is about to expire soon
or has already expired.

Changelog: added
MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/155634
EE: true
---
 Gemfile                                       |  2 +-
 Gemfile.lock                                  |  2 +-
 .../gitlab/expiring_subscription_message.rb   | 31 ++++++++++++
 .../expiring_subscription_message_spec.rb     | 48 ++++++++++++++++++-
 locale/gitlab.pot                             |  6 +++
 5 files changed, 86 insertions(+), 3 deletions(-)

diff --git a/Gemfile b/Gemfile
index 0fd082ee2a43c..7b9c1a77ce47b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -349,7 +349,7 @@ gem 'gon', '~> 6.4.0' # rubocop:todo Gemfile/MissingFeatureCategory
 gem 'request_store', '~> 1.5.1' # rubocop:todo Gemfile/MissingFeatureCategory
 gem 'base32', '~> 0.3.0' # rubocop:todo Gemfile/MissingFeatureCategory
 
-gem 'gitlab-license', '~> 2.4', feature_category: :shared
+gem 'gitlab-license', '~> 2.5', feature_category: :shared
 
 # Protect against bruteforcing
 gem 'rack-attack', '~> 6.7.0' # rubocop:todo Gemfile/MissingFeatureCategory
diff --git a/Gemfile.lock b/Gemfile.lock
index 88348e790f12a..e16cc0192235d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -2004,7 +2004,7 @@ DEPENDENCIES
   gitlab-housekeeper!
   gitlab-http!
   gitlab-labkit (~> 0.36.0)
-  gitlab-license (~> 2.4)
+  gitlab-license (~> 2.5)
   gitlab-mail_room (~> 0.0.24)
   gitlab-markup (~> 1.9.0)
   gitlab-net-dns (~> 0.9.2)
diff --git a/ee/lib/gitlab/expiring_subscription_message.rb b/ee/lib/gitlab/expiring_subscription_message.rb
index f0230b2996c9d..369c2f4b1cda1 100644
--- a/ee/lib/gitlab/expiring_subscription_message.rb
+++ b/ee/lib/gitlab/expiring_subscription_message.rb
@@ -3,6 +3,7 @@
 module Gitlab
   class ExpiringSubscriptionMessage
     GRACE_PERIOD_EXTENSION_DAYS = 30.days
+    TEMP_EXTENSION_EXPIRING_SOON_NOTIFY_WITHIN = 7.days
 
     include Gitlab::Utils::StrongMemoize
     include Gitlab::Routing
@@ -46,6 +47,8 @@ def license_message_subject
     end
 
     def expired_subject
+      return temporary_extension_expired_subject if display_temporary_extension_notification?
+
       if namespace && auto_renew
         _('Something went wrong with your automatic subscription renewal.')
       else
@@ -54,6 +57,8 @@ def expired_subject
     end
 
     def expiring_subject
+      return temporary_extension_expiring_subject if display_temporary_extension_notification?
+
       _('Your %{plan_name} subscription will expire on %{expires_on}') %
         {
           expires_on: subscribable.expires_at.iso8601,
@@ -61,6 +66,18 @@ def expiring_subject
         }
     end
 
+    def temporary_extension_expired_subject
+      _('Your subscription with temporary extension expired!')
+    end
+
+    def temporary_extension_expiring_subject
+      _("Your %{plan_name} subscription with a temporary extension will expire on %{expires_on}") %
+        {
+          expires_on: subscribable.expires_at.iso8601,
+          plan_name: plan_name
+        }
+    end
+
     def expiration_blocking_message
       return '' unless subscribable.will_block_changes?
 
@@ -168,6 +185,7 @@ def self_managed_subscription_future_renewal?
     def require_notification?
       return false if expiring_auto_renew? || ::License.future_dated.present?
       return true if force_notification && subscribable.block_changes?
+      return display_temporary_extension_notification? if temporary_extension?
 
       auto_renew_choice_exists? && expired_subscribable_within_notification_window? && !subscription_future_renewal?
     end
@@ -214,6 +232,19 @@ def self_managed?
       subscribable.is_a?(::License)
     end
 
+    def display_temporary_extension_notification?
+      strong_memoize(:display_temporary_extension_notification) do
+        next false unless temporary_extension?
+
+        expiring_soon = Date.current >= (subscribable.expires_at - TEMP_EXTENSION_EXPIRING_SOON_NOTIFY_WITHIN)
+        subscribable.expired? || expiring_soon ? true : false
+      end
+    end
+
+    def temporary_extension?
+      self_managed? && subscribable.temporary_extension?
+    end
+
     def remaining_days
       strong_memoize(:remaining_days) do
         days = if subscribable.expired?
diff --git a/ee/spec/lib/gitlab/expiring_subscription_message_spec.rb b/ee/spec/lib/gitlab/expiring_subscription_message_spec.rb
index bb1ef2d4856b8..964a93139d737 100644
--- a/ee/spec/lib/gitlab/expiring_subscription_message_spec.rb
+++ b/ee/spec/lib/gitlab/expiring_subscription_message_spec.rb
@@ -11,7 +11,7 @@
     subject(:message) { strip_tags(raw_message) }
 
     let(:subject) { strip_tags(raw_subject) }
-    let(:subscribable) { double(:license) }
+    let(:subscribable) { double(:license, temporary_extension?: false) }
     let(:namespace) { nil }
     let(:force_notification) { false }
     let(:raw_message) do
@@ -320,6 +320,52 @@
                 end
               end
             end
+
+            context 'subscribable is a temporary extension license' do
+              let(:subscribable) { double(:license, temporary_extension?: true) }
+
+              before do
+                allow(subscribable).to receive(:is_a?).with(::License).and_return(true)
+                allow(subscribable).to receive_messages(
+                  will_block_changes?: true,
+                  block_changes_at: block_changes_date,
+                  expired?: expired?
+                )
+              end
+
+              context 'when expiring soon' do
+                let(:expired?) { false }
+
+                it 'has a subject and message indicating the temporary extension is expiring soon' do
+                  expect(subject).to include("Your #{plan_name.capitalize} subscription with a temporary extension will expire on #{expired_date.iso8601}")
+                  expect(message).to include("If you don\'t renew by #{block_changes_date.iso8601} your instance will become read-only, and you won't be able to create issues or merge requests. You will also lose access to your paid features and support entitlement. How do I renew my subscription?")
+                end
+              end
+
+              context 'when already expired' do
+                let(:expired?) { true }
+
+                before do
+                  allow(subscribable).to receive(:block_changes?).and_return(true)
+                end
+
+                it 'has a subject and message indicating the temporary extension has expired' do
+                  expect(subject).to include("Your subscription with temporary extension expired!")
+                  expect(message).to include("This instance is now read-only. Don't worry, your data is safe. To change to GitLab Free and restore write access to this instance, delete your expired license")
+                end
+              end
+
+              context 'when not in notification window' do
+                let(:expired?) { false }
+                let(:expired_date) { (today + 20.days).to_date }
+                let(:block_changes_date) { expired_date }
+
+                it 'does not return a subject and message' do
+                  expect(subject).to be_nil
+                  expect(message).to be_nil
+                end
+              end
+            end
           end
         end
       end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 3a812f5e81469..d969d913d881f 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -61053,6 +61053,9 @@ msgstr ""
 msgid "Your %{plan_name} subscription will expire on %{expires_on}"
 msgstr ""
 
+msgid "Your %{plan_name} subscription with a temporary extension will expire on %{expires_on}"
+msgstr ""
+
 msgid "Your %{plan} plan will be applied to your group."
 msgstr ""
 
@@ -61393,6 +61396,9 @@ msgid_plural "Your subscription has %{remaining_seat_count} out of %{total_seat_
 msgstr[0] ""
 msgstr[1] ""
 
+msgid "Your subscription with temporary extension expired!"
+msgstr ""
+
 msgid "Your top-level group %{namespace_name} has reached the %{free_limit} user limit"
 msgstr ""
 
-- 
GitLab