diff --git a/ee/app/components/gitlab_subscriptions/duo_pro_card_component.rb b/ee/app/components/gitlab_subscriptions/duo_pro_card_component.rb index cb57821fb98962f137fae37ca6c970f4d52aa66b..244787a41eb5e271171e18d891c93f9465070902 100644 --- a/ee/app/components/gitlab_subscriptions/duo_pro_card_component.rb +++ b/ee/app/components/gitlab_subscriptions/duo_pro_card_component.rb @@ -44,11 +44,8 @@ def card_text s_( "CodeSuggestions|Boost productivity across the software development life cycle by using " \ "Code Suggestions and GitLab Duo Chat as part of the %{duoLinkStart}GitLab Duo Pro%{duoLinkEnd} " \ - "add-on. You can now try GitLab Duo Pro for free for %{days} days, no credit card required." + "add-on. You can now try GitLab Duo Pro for free for 60 days, no credit card required." ), - { - days: GitlabSubscriptions::Trials::AddOns::DURATION_NUMBER - }, tag_pair(duo_pro_info_link, :duoLinkStart, :duoLinkEnd) ) end diff --git a/ee/app/controllers/gitlab_subscriptions/trials/duo_enterprise_controller.rb b/ee/app/controllers/gitlab_subscriptions/trials/duo_enterprise_controller.rb index 2ccaba360b4ad4a72c0372b4dbc54599ff85cb9e..e5a63709e1be06f78736fce199d81eb8badfe3f8 100644 --- a/ee/app/controllers/gitlab_subscriptions/trials/duo_enterprise_controller.rb +++ b/ee/app/controllers/gitlab_subscriptions/trials/duo_enterprise_controller.rb @@ -80,15 +80,11 @@ def trial_params end def success_flash_message - safe_format( - s_( - 'DuoEnterpriseTrial|Congratulations, your free GitLab Duo Enterprise trial is activated and will ' \ - 'expire on %{exp_date}. The new license might take a minute to show on the page. ' \ - 'To give members access to new GitLab Duo Enterprise features, ' \ - '%{assign_link_start}assign them%{assign_link_end} to GitLab Duo Enterprise seats.' - ), - success_doc_link, - exp_date: GitlabSubscriptions::Trials::AddOns::DURATION.from_now.to_date.iso8601 + s_( + "DuoEnterpriseTrial|You have successfully created a trial subscription from GitLab Duo Enterprise. " \ + "To get started, enable the GitLab Duo Enterprise add-on for team members on this page by turning on " \ + "the toggle for each team member. The subscription may take a minute to sync, so refresh " \ + "the page if it's not visible yet" ) end end diff --git a/ee/app/controllers/gitlab_subscriptions/trials/duo_pro_controller.rb b/ee/app/controllers/gitlab_subscriptions/trials/duo_pro_controller.rb index c2e65b44e9e76b1dd88e2d26d822f7af299665e0..9bfd42f42eea8998e758b536302d4861e8542542 100644 --- a/ee/app/controllers/gitlab_subscriptions/trials/duo_pro_controller.rb +++ b/ee/app/controllers/gitlab_subscriptions/trials/duo_pro_controller.rb @@ -69,15 +69,11 @@ def trial_params end def success_flash_message - safe_format( - s_( - 'DuoProTrial|Congratulations, your free GitLab Duo Pro trial is activated and will ' \ - 'expire on %{exp_date}. The new license might take a minute to show on the page. ' \ - 'To give members access to new GitLab Duo Pro features, ' \ - '%{assign_link_start}assign them%{assign_link_end} to GitLab Duo Pro seats.' - ), - success_doc_link, - exp_date: GitlabSubscriptions::Trials::AddOns::DURATION.from_now.to_date.iso8601 + s_( + "DuoProTrial|You have successfully created a trial subscription from GitLab Duo Pro. " \ + "To get started, enable the GitLab Duo Pro add-on for team members on this page by turning on " \ + "the toggle for each team member. The subscription may take a minute to sync, so refresh " \ + "the page if it's not visible yet" ) end end diff --git a/ee/app/helpers/billing_plans_helper.rb b/ee/app/helpers/billing_plans_helper.rb index f41080a6f6634a9df184d1f21b7862a369afe0fb..c6a4828e5583689ca7e9526a61bb44287fe00110 100644 --- a/ee/app/helpers/billing_plans_helper.rb +++ b/ee/app/helpers/billing_plans_helper.rb @@ -196,9 +196,7 @@ def duo_enterprise_trial_alert_data(namespace) { title: s_('BillingPlans|Introducing GitLab Duo Enterprise'), body: [ - s_('BillingPlans|Start a GitLab Duo Enterprise trial to try all end-to-end AI capabilities from GitLab. You can try it for free for %{days} days, no credit card required.') % { - days: GitlabSubscriptions::Trials::AddOns::DURATION_NUMBER - } + s_('BillingPlans|Start a GitLab Duo Enterprise trial to try all end-to-end AI capabilities from GitLab. You can try it for free for 60 days, no credit card required.') ] } elsif namespace.premium_plan? diff --git a/ee/app/models/ee/namespace.rb b/ee/app/models/ee/namespace.rb index 4032c7dc18ff6db04f57b3f74ccfafd0d7b5b8af..8292171d248fba40496b6cd48a45e9ea0e4c66c8 100644 --- a/ee/app/models/ee/namespace.rb +++ b/ee/app/models/ee/namespace.rb @@ -421,7 +421,7 @@ def eligible_for_trial? # Be sure to call this on root_ancestor since plans are only associated # with the top-level namespace, not with subgroups. def trial_active? - trial? && trial_ends_on.present? && trial_ends_on >= Date.today + trial? && trial_starts_on.present? && trial_ends_on.present? && trial_ends_on >= Date.today end def never_had_trial? diff --git a/ee/app/models/gitlab_subscriptions/add_on_purchase.rb b/ee/app/models/gitlab_subscriptions/add_on_purchase.rb index 53712b3981792ce0a7030b740f6c1125fb578e35..43d8ca174471e5bf9bf09b5cdb4f10ed8389d7eb 100644 --- a/ee/app/models/gitlab_subscriptions/add_on_purchase.rb +++ b/ee/app/models/gitlab_subscriptions/add_on_purchase.rb @@ -16,6 +16,7 @@ class AddOnPurchase < ApplicationRecord has_many :users, through: :assigned_users validates :add_on, :expires_on, presence: true + validates :add_on, :started_at, presence: true validate :valid_namespace, if: :gitlab_com? validates :subscription_add_on_id, uniqueness: { scope: :namespace_id } validates :quantity, diff --git a/ee/app/models/gitlab_subscriptions/trials/add_ons.rb b/ee/app/models/gitlab_subscriptions/trials/add_ons.rb index a268ad08b7a5bed009e62c235b8ffa61f0d30bfb..31ffa079e97a4dbd637c24ee8411ee1458fe1809 100644 --- a/ee/app/models/gitlab_subscriptions/trials/add_ons.rb +++ b/ee/app/models/gitlab_subscriptions/trials/add_ons.rb @@ -3,9 +3,6 @@ module GitlabSubscriptions module Trials module AddOns - DURATION_NUMBER = 60 - DURATION = DURATION_NUMBER.days - def self.eligible_namespace?(namespace_id, eligible_namespaces) return true if namespace_id.blank? diff --git a/ee/app/presenters/gitlab_subscriptions/trials/duo_pro_status_widget_presenter.rb b/ee/app/presenters/gitlab_subscriptions/trials/duo_pro_status_widget_presenter.rb index 0cc4f3ac66ce499b29e2feadf67a0cffe2332f7e..2bb1c225f9661a8361ea7029814824fa23f33e66 100644 --- a/ee/app/presenters/gitlab_subscriptions/trials/duo_pro_status_widget_presenter.rb +++ b/ee/app/presenters/gitlab_subscriptions/trials/duo_pro_status_widget_presenter.rb @@ -49,9 +49,10 @@ def duo_pro_trial_add_on_purchase strong_memoize_attr :duo_pro_trial_add_on_purchase def trial_status - starts_on = duo_pro_trial_add_on_purchase.expires_on - GitlabSubscriptions::Trials::AddOns::DURATION - - GitlabSubscriptions::TrialStatus.new(starts_on, duo_pro_trial_add_on_purchase.expires_on) + GitlabSubscriptions::TrialStatus.new( + duo_pro_trial_add_on_purchase.started_at, + duo_pro_trial_add_on_purchase.expires_on + ) end strong_memoize_attr :trial_status diff --git a/ee/lib/gitlab/duo/developments/setup.rb b/ee/lib/gitlab/duo/developments/setup.rb index e422c92c00453bf148a229b1939c35510d39d9ae..1f0a7f7dabfa06e1ac00e09907f0602844d86526 100644 --- a/ee/lib/gitlab/duo/developments/setup.rb +++ b/ee/lib/gitlab/duo/developments/setup.rb @@ -202,6 +202,7 @@ def create_duo_pro_purchase!(group) response = ::GitlabSubscriptions::AddOnPurchases::CreateService.new(group, add_on, { quantity: 100, + started_on: Time.current, expires_on: 1.year.from_now, purchase_xid: 'C-12345' }).execute @@ -218,6 +219,7 @@ def create_enterprise_purchase!(group) response = ::GitlabSubscriptions::AddOnPurchases::CreateService.new(group, add_on, { quantity: 100, + started_on: Time.current, expires_on: 1.year.from_now, purchase_xid: 'C-98766' }).execute diff --git a/ee/spec/factories/gitlab_subscriptions/add_on_purchases.rb b/ee/spec/factories/gitlab_subscriptions/add_on_purchases.rb index 14a84d3e339f870a428a1e1ad2b9759ddff1bd96..5ae1e9f87f9379ae328ef8fc07a9a9c7d35d3cce 100644 --- a/ee/spec/factories/gitlab_subscriptions/add_on_purchases.rb +++ b/ee/spec/factories/gitlab_subscriptions/add_on_purchases.rb @@ -6,8 +6,8 @@ organization { namespace ? namespace.organization : association(:organization, :default) } namespace { association(:group) } quantity { 1 } - started_at { 1.day.ago.to_date } - expires_on { 1.year.from_now.to_date } + started_at { Time.current } + expires_on { 60.days.from_now } purchase_xid { SecureRandom.hex(16) } trial { false } @@ -18,7 +18,7 @@ trait :trial do trial { true } - expires_on { GitlabSubscriptions::Trials::AddOns::DURATION.from_now } + expires_on { 60.days.from_now.to_date } end trait :active_trial do diff --git a/ee/spec/helpers/billing_plans_helper_spec.rb b/ee/spec/helpers/billing_plans_helper_spec.rb index c37fb3b474052e2e5a32e008f386a487d8eff381..181c15049f1b459aa10039978b919650e5d4a90f 100644 --- a/ee/spec/helpers/billing_plans_helper_spec.rb +++ b/ee/spec/helpers/billing_plans_helper_spec.rb @@ -309,7 +309,11 @@ let(:namespace) do build( :group, - gitlab_subscription: build(:gitlab_subscription, trial: trial_active, trial_ends_on: 1.week.from_now)) + gitlab_subscription: + build(:gitlab_subscription, + trial: trial_active, + trial_starts_on: Time.current, + trial_ends_on: 1.week.from_now)) end subject { helper.upgrade_button_css_classes(namespace, plan_data, is_current_plan) } diff --git a/ee/spec/models/gitlab_subscriptions/add_on_purchase_spec.rb b/ee/spec/models/gitlab_subscriptions/add_on_purchase_spec.rb index be2944840ed606dbca156b7e227efa85d63ade14..0645a9f77a88a582f6ee1dafd2c9ee86ab2f856b 100644 --- a/ee/spec/models/gitlab_subscriptions/add_on_purchase_spec.rb +++ b/ee/spec/models/gitlab_subscriptions/add_on_purchase_spec.rb @@ -25,6 +25,7 @@ describe 'validations' do it { is_expected.to validate_presence_of(:add_on) } it { is_expected.to validate_presence_of(:expires_on) } + it { is_expected.to validate_presence_of(:started_at) } context 'when validating namespace' do context 'when on .com', :saas do @@ -685,18 +686,6 @@ context 'when subscription has expired' do it { travel_to(add_on_purchase.expires_on + 1.day) { is_expected.to eq(false) } } end - - context 'when subscription has not started yet' do - context 'when started_at is nil' do - before do - add_on_purchase.update!(started_at: nil) - end - - it { is_expected.to eq(true) } - end - - it { travel_to(add_on_purchase.started_at - 1.day) { is_expected.to eq(false) } } - end end describe '#expired?' do diff --git a/ee/spec/models/gitlab_subscriptions/trials/duo_pro_status_spec.rb b/ee/spec/models/gitlab_subscriptions/trials/duo_pro_status_spec.rb index 5b866f32b480f3a490bd26f0652de11a8ca0e47e..d8967c0cd125ca65d5334e951126d2448b7556b2 100644 --- a/ee/spec/models/gitlab_subscriptions/trials/duo_pro_status_spec.rb +++ b/ee/spec/models/gitlab_subscriptions/trials/duo_pro_status_spec.rb @@ -41,19 +41,20 @@ context 'with an expired ultimate trial' do let(:trial_starts_on) { 2.days.ago } let(:trial_ends_on) { 1.day.ago } - let(:duo_pro_exprires_on) { 1.day.ago + GitlabSubscriptions::Trials::AddOns::DURATION } + let(:duo_pro_expires_on) { 1.day.ago } + let(:trial_duration) { 60 } before do gitlab_subscription = build(:gitlab_subscription, :ultimate, namespace: group) gitlab_subscription.trial_starts_on = trial_starts_on gitlab_subscription.trial_ends_on = trial_ends_on - add_on_purchase.expires_on = duo_pro_exprires_on + add_on_purchase.expires_on = duo_pro_expires_on end it { is_expected.to be(true) } context 'with an expired duo pro trial add on' do - let(:duo_pro_exprires_on) { 1.day.ago } + let(:duo_pro_expires_on) { trial_duration.days.ago + 1.day.ago } it { is_expected.to be(true) } end diff --git a/ee/spec/presenters/gitlab_subscriptions/trials/duo_pro_status_widget_presenter_spec.rb b/ee/spec/presenters/gitlab_subscriptions/trials/duo_pro_status_widget_presenter_spec.rb index 4faad2602ea3c900c0e29a8f6f976a7666bdcb54..0cf31928e41bd6460629427dc44ae95e6ab1af83 100644 --- a/ee/spec/presenters/gitlab_subscriptions/trials/duo_pro_status_widget_presenter_spec.rb +++ b/ee/spec/presenters/gitlab_subscriptions/trials/duo_pro_status_widget_presenter_spec.rb @@ -21,22 +21,25 @@ describe '#attributes' do subject { described_class.new(group, user: user).attributes } + let(:trial_duration) { 60 } + specify do freeze_time do # set here to ensure no date barrier flakiness - add_on_purchase.expires_on = 60.days.from_now + add_on_purchase.started_at = Time.current + add_on_purchase.expires_on = trial_duration.days.from_now duo_pro_trial_status_widget_data_attrs = { trial_days_used: 1, - trial_duration: 60, + trial_duration: trial_duration, percentage_complete: 1.67, group_id: group.id, feature_id: described_class::EXPIRED_DUO_PRO_TRIAL_WIDGET, dismiss_endpoint: group_callouts_path } duo_pro_trial_status_popover_data_attrs = { - days_remaining: 60, - trial_end_date: 60.days.from_now.to_date, + days_remaining: trial_duration, + trial_end_date: trial_duration.days.from_now.to_date, purchase_now_url: ::Gitlab::Routing.url_helpers.group_settings_gitlab_duo_usage_index_path(group), learn_about_button_url: diff --git a/ee/spec/requests/gitlab_subscriptions/trials/duo_enterprise_controller_spec.rb b/ee/spec/requests/gitlab_subscriptions/trials/duo_enterprise_controller_spec.rb index 918efde28646fc878ca7b7a3d87e3761fd2902d5..14866378078f1948960b2463d8d8b3df2caa1329 100644 --- a/ee/spec/requests/gitlab_subscriptions/trials/duo_enterprise_controller_spec.rb +++ b/ee/spec/requests/gitlab_subscriptions/trials/duo_enterprise_controller_spec.rb @@ -190,19 +190,10 @@ it 'shows valid flash message', :freeze_time do post_create - expires_on = GitlabSubscriptions::Trials::AddOns::DURATION.from_now.to_date.iso8601 - message = format( - s_( - 'DuoEnterpriseTrial|Congratulations, your free GitLab Duo Enterprise trial is activated and will ' \ - 'expire on %{exp_date}. The new license might take a minute to show on the page. To give members ' \ - 'access to new GitLab Duo Enterprise features, <a target="_blank" rel="noopener noreferrer" ' \ - 'href="/help/subscriptions/subscription-add-ons#assign-gitlab-duo-seats">assign them</a> ' \ - 'to GitLab Duo Enterprise seats.' - ), - exp_date: expires_on - ) - - expect(flash[:success]).to eq(message) + expect(flash[:success]).to eq( + "You have successfully created a trial subscription from GitLab Duo Enterprise. To get started, enable " \ + "the GitLab Duo Enterprise add-on for team members on this page by turning on the toggle for each team " \ + "member. The subscription may take a minute to sync, so refresh the page if it's not visible yet") end end diff --git a/ee/spec/requests/gitlab_subscriptions/trials/duo_pro_controller_spec.rb b/ee/spec/requests/gitlab_subscriptions/trials/duo_pro_controller_spec.rb index 8e892f19fe8c1a16c61624d6341df03cf234c29c..b59ce49a1bd08c6811d0ec90912da3ed56b5ebe5 100644 --- a/ee/spec/requests/gitlab_subscriptions/trials/duo_pro_controller_spec.rb +++ b/ee/spec/requests/gitlab_subscriptions/trials/duo_pro_controller_spec.rb @@ -171,16 +171,10 @@ it 'shows valid flash message', :freeze_time do post_create - expires_on = 60.days.from_now.to_date.iso8601 expect(flash[:success]).to eq( - format(s_( - 'DuoProTrial|Congratulations, your free GitLab Duo Pro trial is activated and will expire on ' \ - '%{exp_date}. The new license might take a minute to show on the page. To give members access to new ' \ - 'GitLab Duo Pro features, <a target="_blank" rel="noopener noreferrer" ' \ - 'href="/help/subscriptions/subscription-add-ons#assign-gitlab-duo-seats">assign them</a> ' \ - 'to GitLab Duo Pro seats.' - ), exp_date: expires_on) - ) + "You have successfully created a trial subscription from GitLab Duo Pro. To get started, enable " \ + "the GitLab Duo Pro add-on for team members on this page by turning on the toggle for each team " \ + "member. The subscription may take a minute to sync, so refresh the page if it's not visible yet") end context 'when feature flag duo_enterprise_trials is disabled' do diff --git a/ee/spec/views/groups/billings/index.html.haml_spec.rb b/ee/spec/views/groups/billings/index.html.haml_spec.rb index 8befb094012fe5f31debbfdbdbe07f8ed956388b..d1ed30bbfbad0c8422cf0b91866090648f751028 100644 --- a/ee/spec/views/groups/billings/index.html.haml_spec.rb +++ b/ee/spec/views/groups/billings/index.html.haml_spec.rb @@ -128,7 +128,12 @@ def expect_to_have_tracking(action:, label: nil) end context 'with an expired trial' do - let_it_be(:group) { create(:group_with_plan, plan: :free_plan, trial_ends_on: Date.yesterday) } + let_it_be(:group) do + create(:group_with_plan, + plan: :free_plan, + trial_starts_on: Time.current, + trial_ends_on: Date.yesterday) + end it_behaves_like 'without ultimate trial cta alert' it_behaves_like 'without duo pro component' @@ -146,7 +151,12 @@ def expect_to_have_tracking(action:, label: nil) end context 'with an active trial' do - let_it_be(:group) { create(:group_with_plan, plan: :ultimate_trial_plan, trial_ends_on: 10.days.from_now) } + let_it_be(:group) do + create(:group_with_plan, + plan: :ultimate_trial_plan, + trial_starts_on: Time.current, + trial_ends_on: 10.days.from_now) + end it_behaves_like 'without ultimate trial cta alert' it_behaves_like 'without duo pro component' diff --git a/ee/spec/views/shared/billings/_trial_status.html.haml_spec.rb b/ee/spec/views/shared/billings/_trial_status.html.haml_spec.rb index 6689a568a862aaa6e1553adaaa82600d278829e8..187a40625b53d2d6322c55054e94d52d20a326ba 100644 --- a/ee/spec/views/shared/billings/_trial_status.html.haml_spec.rb +++ b/ee/spec/views/shared/billings/_trial_status.html.haml_spec.rb @@ -14,7 +14,7 @@ before do allow(group).to receive(:eligible_for_trial?).and_return(false) - create(:gitlab_subscription, namespace: group, hosted_plan: plan, trial_ends_on: trial_ends_on, trial: trial) + create(:gitlab_subscription, namespace: group, hosted_plan: plan, trial_starts_on: Time.current, trial_ends_on: trial_ends_on, trial: trial) end context 'when not eligible for trial' do diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 74fc5374a811ac45a5028cf36bbe28bd5fccc7cf..7ae5f1975e8b399782e74c2daf566cf426c80ce3 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -8751,7 +8751,7 @@ msgstr "" msgid "BillingPlans|See all %{plan_name} features" msgstr "" -msgid "BillingPlans|Start a GitLab Duo Enterprise trial to try all end-to-end AI capabilities from GitLab. You can try it for free for %{days} days, no credit card required." +msgid "BillingPlans|Start a GitLab Duo Enterprise trial to try all end-to-end AI capabilities from GitLab. You can try it for free for 60 days, no credit card required." msgstr "" msgid "BillingPlans|Start a free GitLab Duo Enterprise Trial" @@ -13376,7 +13376,7 @@ msgstr "" msgid "CodeSuggestions|Before you can buy GitLab Duo seats, you'll need a Premium or Ultimate subscription." msgstr "" -msgid "CodeSuggestions|Boost productivity across the software development life cycle by using Code Suggestions and GitLab Duo Chat as part of the %{duoLinkStart}GitLab Duo Pro%{duoLinkEnd} add-on. You can now try GitLab Duo Pro for free for %{days} days, no credit card required." +msgid "CodeSuggestions|Boost productivity across the software development life cycle by using Code Suggestions and GitLab Duo Chat as part of the %{duoLinkStart}GitLab Duo Pro%{duoLinkEnd} add-on. You can now try GitLab Duo Pro for free for 60 days, no credit card required." msgstr "" msgid "CodeSuggestions|Code Explanations" @@ -20241,9 +20241,6 @@ msgstr "" msgid "DuoEnterpriseTrial|Apply your GitLab Duo Enterprise trial to an existing group" msgstr "" -msgid "DuoEnterpriseTrial|Congratulations, your free GitLab Duo Enterprise trial is activated and will expire on %{exp_date}. The new license might take a minute to show on the page. To give members access to new GitLab Duo Enterprise features, %{assign_link_start}assign them%{assign_link_end} to GitLab Duo Enterprise seats." -msgstr "" - msgid "DuoEnterpriseTrial|Enhance security and remediate vulnerabilities efficiently" msgstr "" @@ -20280,6 +20277,9 @@ msgstr "" msgid "DuoEnterpriseTrial|We just need some additional information to activate your trial." msgstr "" +msgid "DuoEnterpriseTrial|You have successfully created a trial subscription from GitLab Duo Enterprise. To get started, enable the GitLab Duo Enterprise add-on for team members on this page by turning on the toggle for each team member. The subscription may take a minute to sync, so refresh the page if it's not visible yet" +msgstr "" + msgid "DuoFreeAccessEndingBanner|Buy Duo Pro" msgstr "" @@ -20394,9 +20394,6 @@ msgstr "" msgid "DuoProTrial|Code completion and code generation with Code Suggestions" msgstr "" -msgid "DuoProTrial|Congratulations, your free GitLab Duo Pro trial is activated and will expire on %{exp_date}. The new license might take a minute to show on the page. To give members access to new GitLab Duo Pro features, %{assign_link_start}assign them%{assign_link_end} to GitLab Duo Pro seats." -msgstr "" - msgid "DuoProTrial|Day %{daysUsed}/%{duration}" msgstr "" @@ -20448,6 +20445,9 @@ msgstr "" msgid "DuoProTrial|You do not have access to trial GitLab Duo" msgstr "" +msgid "DuoProTrial|You have successfully created a trial subscription from GitLab Duo Pro. To get started, enable the GitLab Duo Pro add-on for team members on this page by turning on the toggle for each team member. The subscription may take a minute to sync, so refresh the page if it's not visible yet" +msgstr "" + msgid "DuoProTrial|You no longer have access to GitLab Duo Pro features" msgstr ""