Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
24.04% covered (danger)
24.04%
25 / 104
9.09% covered (danger)
9.09%
1 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
Gifting_Banner
25.00% covered (danger)
25.00%
25 / 100
9.09% covered (danger)
9.09%
1 / 11
492.42
0.00% covered (danger)
0.00%
0 / 1
 init
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
6.84
 register_gifting_banner
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 should_display_expiring_plan_notice
93.75% covered (success)
93.75%
15 / 16
0.00% covered (danger)
0.00%
0 / 1
10.02
 get_checkout_link
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_days_to_dismiss_banner
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 get_more_info_link
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 inject_gifting_banner_wpcom
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 inject_gifting_banner_wpcomsh
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
2
 get_plan_purchase
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 get_title_texts
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 get_subtitle_texts
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
20
1<?php // phpcs:ignore WordPress.File.FileName.InvalidClassFileName
2/**
3 * Show the gifting banner on Simple & Atomic sites.
4 * This file is duplicated in WPCOM and WPCOMSH.
5 * WPCOM: public_html/wp-content/blog-plugins/gifting-banner.php
6 * WPCOMSH: wpcomsh/frontend-notices/gifting-banner/gifting-banner.php
7 * See: p9Jlb4-5v7-p2
8 *
9 * @package gifting-banner
10 */
11
12/**
13 * Class Gifting_Banner
14 */
15class Gifting_Banner {
16
17    /**
18     * The current purchased plan of the blog.
19     * Used to pass data between methods.
20     *
21     * @var object|null
22     */
23    public $current_plan;
24
25    /**
26     * Maybe show the gifting banner for the current site.
27     */
28    public function init() {
29        // Inject the gifting banner after the launch banner.
30        if ( defined( 'IS_ATOMIC' ) && IS_ATOMIC ) {
31            // The expiry banner takes the same spot for admins; one bar at a time.
32            if ( function_exists( 'wpcom_expiry_notices_frontend_banner_is_due' ) && wpcom_expiry_notices_frontend_banner_is_due() ) {
33                return;
34            }
35            if ( ! $this->should_display_expiring_plan_notice() ) {
36                return;
37            }
38
39            add_action( 'wp_head', array( $this, 'inject_gifting_banner_wpcomsh' ), 1103 );
40        } else {
41            add_action( 'wp_head', array( $this, 'inject_gifting_banner_wpcom' ), 1103 );
42        }
43    }
44
45    /**
46     * Resolves if the gifting banner should be displayed and adds it to the banner list in necessary.
47     *
48     * @param array $banners Banners.
49     *
50     * @return array
51     */
52    public function register_gifting_banner( $banners ) {
53        // If the banner shouldn't display, don't inject it.
54        if ( ! $this->should_display_expiring_plan_notice() ) {
55            return $banners;
56        }
57
58        return array_merge( $banners, array( 'wpcom_gifting_banner' => array( $this, 'init' ) ) );
59    }
60
61    /**
62     * Determines if the plan expiring notice should display.
63     *
64     * @return bool
65     */
66    public function should_display_expiring_plan_notice() {
67        if ( function_exists( 'is_automattic' ) && is_automattic() ) {
68            return false;
69        }
70
71        $this->current_plan = static::get_plan_purchase();
72
73        // If site doesn't have valid plan -> don't show the banner.
74        if ( ! $this->current_plan ) {
75            return false;
76        }
77
78        // If wpcom_gifting_subscription option exists, we show/hide the banner based on it.
79        if ( 'option-not-exists' !== get_option( 'wpcom_gifting_subscription', 'option-not-exists' ) ) {
80            return (bool) get_option( 'wpcom_gifting_subscription' );
81        }
82
83        // Create parity between WPCOM and WPCOMSH for auto_renew.
84        if ( defined( 'IS_ATOMIC' ) && IS_ATOMIC ) {
85            $this->current_plan->user_allows_auto_renew = $this->current_plan->auto_renew;
86        }
87
88        // Test if gifting is enabled - We default to the inverse of auto-renew but configured options take precedence.
89        if ( ! get_option( 'wpcom_gifting_subscription', ! $this->current_plan->user_allows_auto_renew ) ) {
90            return false;
91        }
92
93        /*
94         * We will display the banner:
95         * - 54 days before the annual plan expiration.
96         * - 5 days before the monthly plan expiration.
97         */
98        $days_of_warning          = false !== strpos( $this->current_plan->product_slug, 'monthly' ) ? 5 : 54;
99        $seconds_until_expiration = strtotime( $this->current_plan->expiry_date ) - time();
100
101        if ( $seconds_until_expiration < $days_of_warning * DAY_IN_SECONDS ) {
102            // Show the banner.
103            return true;
104        }
105
106        return false;
107    }
108
109    /**
110     * Get checkout link.
111     */
112    public function get_checkout_link() {
113        return 'https://wordpress.com/checkout/' . $this->current_plan->product_slug . '/gift/' . $this->current_plan->subscription_id . '?cancel_to=/home';
114    }
115
116    /**
117     * Get how many days the banner should be dismissed based on plan type (30 days for monthly, 1 year for yearly)
118     */
119    public function get_days_to_dismiss_banner() {
120        return false !== strpos( $this->current_plan->product_slug, 'monthly' ) ? 30 : 365;
121    }
122
123    /**
124     * Get more info link.
125     */
126    public function get_more_info_link() {
127        return 'https://wordpress.com/support/gift-a-wordpress-com-subscription/';
128    }
129
130    /**
131     * Inject the gifting banner on WPCOM.
132     */
133    public function inject_gifting_banner_wpcom() {
134        $days_to_expire             = ceil( ( strtotime( $this->current_plan->expiry_date ) - time() ) / DAY_IN_SECONDS );
135        $data                       = array();
136        $data['dismiss_days_count'] = $this->get_days_to_dismiss_banner();
137        $data['checkout_link']      = $this->get_checkout_link();
138        $data['more_info_link']     = localized_wpcom_url( $this->get_more_info_link() );
139        $data['i18n']               = array(
140            'title'       => $this->get_title_texts( $days_to_expire ),
141            'subtitle'    => $this->get_subtitle_texts( $this->current_plan, $days_to_expire ),
142            'button_text' => _x( 'Gift', 'verb', 'wpcomsh' ),
143        );
144        // Change the version if associated files are updated, current: 20230103.
145        wp_enqueue_style( 'gifting-banner', plugins_url( 'gifting-banner/css/gifting-banner.css', __FILE__ ), array(), '20230103' );
146        wp_enqueue_script( 'gifting-banner', plugins_url( 'gifting-banner/js/gifting-banner.js', __FILE__ ), array(), '20230103', true );
147        wp_localize_script( 'gifting-banner', 'gifting_banner', $data );
148        wp_set_script_translations( 'gifting-banner', 'wpcomsh' );
149    }
150
151    /**
152     * Inject the gifting banner on WPCOMSH.
153     */
154    public function inject_gifting_banner_wpcomsh() {
155        $days_to_expire             = ceil( ( strtotime( $this->current_plan->expiry_date ) - time() ) / DAY_IN_SECONDS );
156        $data                       = array();
157        $data['dismiss_days_count'] = $this->get_days_to_dismiss_banner();
158        $data['checkout_link']      = $this->get_checkout_link();
159        $data['more_info_link']     = $this->get_more_info_link();
160        $data['i18n']               = array(
161            'title'       => static::get_title_texts( $days_to_expire ),
162            'subtitle'    => static::get_subtitle_texts( $this->current_plan, $days_to_expire ),
163            'button_text' => _x(
164                'Gift',
165                'verb',
166                'wpcomsh'
167            ),
168        );
169
170        wp_enqueue_style( 'gifting-banner', plugins_url( 'css/gifting-banner.css', __FILE__ ), array(), WPCOMSH_VERSION );
171        wp_enqueue_script( 'gifting-banner', plugins_url( 'js/gifting-banner.js', __FILE__ ), array(), WPCOMSH_VERSION, true );
172        wp_localize_script( 'gifting-banner', 'gifting_banner', $data );
173        wp_set_script_translations( 'gifting-banner', 'wpcomsh' );
174    }
175
176    /**
177     * Get hosting plan purchase from the current site.
178     * See: https://github.com/Automattic/wpcomsh/pull/1118
179     *
180     * @return object|null
181     */
182    private static function get_plan_purchase() {
183        $purchases = wpcom_get_site_purchases();
184
185        foreach ( $purchases as $purchase ) {
186            if ( wpcom_purchase_has_feature( $purchase, WPCOM_Features::SUBSCRIPTION_GIFTING ) ) {
187                return $purchase;
188            }
189        }
190
191        return null;
192    }
193
194    /**
195     * Get title based on days.
196     *
197     * @param int $days_to_expire Days to expire.
198     * @return string
199     */
200    private function get_title_texts( $days_to_expire ) {
201
202        if ( $days_to_expire < 1 ) {
203            return __(
204                'This site\'s plan has expired.',
205                'wpcomsh'
206            );
207        }
208
209        return __(
210            'Enjoy this site?',
211            'wpcomsh'
212        );
213    }
214
215    /**
216     * Get subtitle based on days & type of plan.
217     * - Plan expired
218     * - Annual Plan < 2 weeks before expiration
219     * - Monthly Plan or Annual plan > 2 weeks before expiration
220     *
221     * @param object $current_plan Current Plan.
222     * @param int    $days_to_expire Days to expire.
223     * @return string
224     */
225    private function get_subtitle_texts( $current_plan, $days_to_expire ) {
226
227        if ( $days_to_expire < 1 ) {
228            return sprintf(
229            /* translators: Banner to show the visitor the site gifting option on expired sites. */
230                __(
231                    'Gift the author a WordPress.com upgrade.',
232                    'wpcomsh'
233                ),
234                $days_to_expire
235            );
236        }
237
238        if ( ! strpos( $current_plan->product_slug, 'monthly' ) && $days_to_expire < 15 ) {
239            return sprintf(
240            /* translators: Banner to show the visitor the site gifting option on days before expires. */
241                _n(
242                    'Gift the author a WordPress.com plan before it expires in %d day.',
243                    'Gift the author a WordPress.com plan before it expires in %d days.',
244                    $days_to_expire,
245                    'wpcomsh'
246                ),
247                $days_to_expire
248            );
249        }
250
251        return sprintf(
252        /* translators: Banner to show the visitor the site gifting option, no days shown. */
253            __(
254                'Gift the author a WordPress.com plan.',
255                'wpcomsh'
256            ),
257            $days_to_expire
258        );
259    }
260}
261
262/**
263 * Load the Gifting Banner.
264 */
265$gifting_banner = new Gifting_Banner();
266
267// On Atomic sites we don't have a banner resolver, so we apply the filter directly.
268if ( defined( 'IS_ATOMIC' ) && IS_ATOMIC ) {
269    add_action( 'init', array( $gifting_banner, 'init' ) );
270} else {
271    add_filter( 'wpcom_register_banners', array( $gifting_banner, 'register_gifting_banner' ) );
272}